part of 'player_page.dart'; bool _isDanmakuLayoutChange(DanmakuPanelSettings a, DanmakuPanelSettings b) { return a.maxRows != b.maxRows || a.fontSize != b.fontSize || a.lineHeight != b.lineHeight || a.fontFamily != b.fontFamily || a.area != b.area; } void _syncDanmakuFeederForSession( DanmakuFeeder feeder, PlaybackSession session, double posSec, { bool clear = false, }) { feeder.seekTo(posSec, clear: clear); if (session.isDanmakuActive) { feeder.playFrom(posSec); } else { feeder.freezeAt(posSec); } } extension _PlayerPageDanmaku on _PlayerPageState { DanmakuOption _buildDanmakuOption(DanmakuPanelSettings settings) { final base = settings.toDanmakuOption(screenHeight: _danmakuOverlayHeight); if (_playbackRate == 1.0 || _playbackRate <= 0) return base; return base.copyWith(duration: base.duration / _playbackRate); } void _applyDanmakuPanelSettings(DanmakuPanelSettings settings) { _panelSettings = settings; _feeder.setPanelSettings(settings, option: _buildDanmakuOption(settings)); } void _wireDanmakuListeners() { final session = ref.read(danmakuSessionProvider).value; if (session != null) { _sessionState = session; if (session is DanmakuSessionMatched && session.commentStatus == DanmakuCommentStatus.ready && !identical(session, _lastSyncedDanmakuSession)) { _lastSyncedDanmakuSession = session; _feeder.setComments(session.comments); final s = _session; if (s != null) { _syncDanmakuFeederForSession( _feeder, s, s.engine.state.position.inMilliseconds / 1000.0, ); } } } final settingsPanel = ref .read(danmakuSettingsProvider) .value ?.panelSettings; if (settingsPanel != null && settingsPanel != _panelSettings) { _applyDanmakuPanelSettings(settingsPanel); } ref.listen>(danmakuSessionProvider, ( prev, next, ) { final state = next.value; if (state == null) return; _sessionState = state; if (state is DanmakuSessionMatched && state.commentStatus == DanmakuCommentStatus.ready) { _lastSyncedDanmakuSession = state; _feeder.setComments(state.comments); final s = _session; if (s != null) { final posSec = s.engine.state.position.inMilliseconds / 1000.0; _syncDanmakuFeederForSession(_feeder, s, posSec); } } else { _lastSyncedDanmakuSession = null; _feeder.setComments(const []); } _invalidateBottomBar(); _refreshPanel(); _invalidateDanmakuOverlay(); }); ref.listen>(danmakuSettingsProvider, ( prev, next, ) { final settings = next.value; if (settings == null) return; final newPanel = settings.panelSettings; final oldPanel = _panelSettings; final panelChanged = oldPanel != newPanel; final previousSettings = prev?.value; final keywordsChanged = previousSettings != null && !listEquals( previousSettings.blockedKeywords, settings.blockedKeywords, ); final enabledChanged = oldPanel.enabled != newPanel.enabled; final offsetChanged = oldPanel.offset != newPanel.offset; final layoutChanged = _isDanmakuLayoutChange(oldPanel, newPanel); final opacityChanged = oldPanel.opacity != newPanel.opacity; if (panelChanged) { _applyDanmakuPanelSettings(newPanel); if (enabledChanged) { _invalidateBottomBar(); } _invalidateDanmakuOverlay(); final s = _session; if ((enabledChanged || offsetChanged || layoutChanged || opacityChanged) && s != null) { final posSec = s.engine.state.position.inMilliseconds / 1000.0; _syncDanmakuFeederForSession(_feeder, s, posSec, clear: true); } } if (panelChanged || keywordsChanged) { _refreshPanel(); } }); } bool get _danmakuLoaded => _sessionState is DanmakuSessionMatched || _sessionState is DanmakuSessionEmpty; bool get _danmakuSearching => _sessionState is DanmakuSessionMatching || (_sessionState is DanmakuSessionMatched && (_sessionState as DanmakuSessionMatched).commentStatus == DanmakuCommentStatus.loading); List get _danmakuMatches { final s = _sessionState; return s is DanmakuSessionMatched ? s.matches : const []; } int get _danmakuSelectedMatchIndex { final s = _sessionState; return s is DanmakuSessionMatched ? s.selectedIndex : -1; } int get _danmakuCommentCount { final s = _sessionState; return s is DanmakuSessionMatched ? s.comments.length : 0; } }