diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 9e6a3db15..c04d1c700 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -57,7 +57,7 @@ class _PlDanmakuState extends State { } else { _plDanmakuController.queryDanmaku( PlDanmakuController.calcSegment( - playerController.position.value.inMilliseconds, + playerController.position.inMilliseconds, ), ); } diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index dafe039f6..3dad4678a 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -499,8 +499,7 @@ class VideoDetailController extends GetxController bool get preInitPlayer => plPlayerController.preInitPlayer; @override int get currPosInMilliseconds => - defaultST?.inMilliseconds ?? - plPlayerController.position.value.inMilliseconds; + defaultST?.inMilliseconds ?? plPlayerController.position.inMilliseconds; @override Future seekTo(Duration duration, {required bool isSeek}) => plPlayerController.seekTo(duration, isSeek: isSeek); @@ -583,7 +582,7 @@ class VideoDetailController extends GetxController return SendDanmakuPanel( cid: cid.value, bvid: bvid, - progress: plPlayerController.position.value.inMilliseconds, + progress: plPlayerController.position.inMilliseconds, initialValue: savedDanmaku, onSave: (danmaku) => savedDanmaku = danmaku, onSuccess: (danmakuModel) { @@ -634,7 +633,7 @@ class VideoDetailController extends GetxController final currentVideoQa = this.currentVideoQa.value; if (currentVideoQa == null) return; _autoPlay.value = true; - playedTime = plPlayerController.position.value; + playedTime = plPlayerController.position; plPlayerController ..removeListeners() ..isBuffering.value = false @@ -960,7 +959,7 @@ class VideoDetailController extends GetxController PostSegmentModel( segment: Pair( first: 0, - second: plPlayerController.position.value.inMilliseconds / 1000, + second: plPlayerController.position.inMilliseconds / 1000, ), category: SegmentType.sponsor, actionType: ActionType.skip, @@ -1177,7 +1176,7 @@ class VideoDetailController extends GetxController ? -1 : playedTime!.inSeconds : playedTime!.inSeconds, - type: HeartBeatType.status, + type: HeartBeatType.completed, isManual: true, aid: aid, bvid: bvid, diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index 25f6be9c2..bae1e1ed0 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -184,10 +184,9 @@ class _PostPanelState extends State late final List list = videoDetailController.postList; late final double videoDuration = - plPlayerController.durationSeconds.value.inMilliseconds / 1000; + plPlayerController.duration.value.inMilliseconds / 1000; - double get currentPos => - plPlayerController.position.value.inMilliseconds / 1000; + double currentPos() => plPlayerController.position.inMilliseconds / 1000; @override Widget buildPage(ThemeData theme) { @@ -211,7 +210,7 @@ class _PostPanelState extends State PostSegmentModel( segment: Pair( first: 0, - second: currentPos, + second: currentPos(), ), category: SegmentType.sponsor, actionType: ActionType.skip, @@ -350,7 +349,7 @@ class _PostPanelState extends State PostPanel.segmentWidget( theme, item: item, - currentPos: () => currentPos, + currentPos: currentPos, videoDuration: videoDuration, ), Wrap( diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 1d95a9de1..15503807f 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -83,18 +83,17 @@ class PlPlayerController with BlockConfigMixin { /// 响应数据 /// 带有Seconds的变量只在秒数更新时更新,以避免频繁触发重绘 // 播放位置 - final Rx position = Rx(Duration.zero); + Duration position = Duration.zero; final RxInt positionSeconds = 0.obs; /// 进度条位置 - final Rx sliderPosition = Rx(Duration.zero); + Duration sliderPosition = Duration.zero; final RxInt sliderPositionSeconds = 0.obs; // 展示使用 final Rx sliderTempPosition = Rx(Duration.zero); /// 视频时长 final Rx duration = Rx(Duration.zero); - final Rx durationSeconds = Duration.zero.obs; /// 视频缓冲 final Rx buffered = Rx(Duration.zero); @@ -118,9 +117,6 @@ class PlPlayerController with BlockConfigMixin { /// 是否展示控制条 final RxBool showControls = false.obs; - /// 音量控制条展示/隐藏 - final RxBool showVolumeStatus = false.obs; - /// 亮度控制条展示/隐藏 final RxBool showBrightnessStatus = false.obs; @@ -182,7 +178,6 @@ class PlPlayerController with BlockConfigMixin { Timer? _timer; Timer? _timerForSeek; - Timer? _timerForShowingVolume; Box setting = GStorage.setting; @@ -460,25 +455,19 @@ class PlPlayerController with BlockConfigMixin { } void updateSliderPositionSecond() { - int newSecond = sliderPosition.value.inSeconds; + int newSecond = sliderPosition.inSeconds; if (sliderPositionSeconds.value != newSecond) { sliderPositionSeconds.value = newSecond; } } void updatePositionSecond() { - int newSecond = position.value.inSeconds; + int newSecond = position.inSeconds; if (positionSeconds.value != newSecond) { positionSeconds.value = newSecond; } } - void updateDurationSecond() { - if (durationSeconds.value != duration.value) { - durationSeconds.value = duration.value; - } - } - void updateBufferedSecond() { int newSecond = buffered.value.inSeconds; if (bufferedSeconds.value != newSecond) { @@ -653,9 +642,7 @@ class PlPlayerController with BlockConfigMixin { ); // 获取视频时长 00:00 this.duration.value = duration ?? _videoPlayerController!.state.duration; - position.value = buffered.value = sliderPosition.value = - seekTo ?? Duration.zero; - updateDurationSecond(); + position = buffered.value = sliderPosition = seekTo ?? Duration.zero; updatePositionSecond(); updateSliderPositionSecond(); updateBufferedSecond(); @@ -766,7 +753,7 @@ class PlPlayerController with BlockConfigMixin { isBuffering.value = false; buffered.value = Duration.zero; _heartDuration = 0; - position.value = Duration.zero; + position = Duration.zero; // 初始化时清空弹幕,防止上次重叠 danmakuController?.clear(); @@ -817,6 +804,8 @@ class PlPlayerController with BlockConfigMixin { } } + final Map extras = {}; + // 音轨 final String audioUri; if (isFileSource) { @@ -825,12 +814,12 @@ class PlPlayerController with BlockConfigMixin { : path.join(dirPath!, typeTag!, PathUtils.audioNameType2); } else if (dataSource.audioSource?.isNotEmpty == true) { audioUri = Platform.isWindows - ? dataSource.audioSource!.replaceAll(';', '\\;') - : dataSource.audioSource!.replaceAll(':', '\\:'); + ? dataSource.audioSource!.replaceAll(';', r'\;') + : dataSource.audioSource!.replaceAll(':', r'\:'); } else { audioUri = ''; } - await pp.setProperty('audio-files', audioUri); + if (audioUri.isNotEmpty) extras['audio-files'] = '"$audioUri"'; _videoController ??= VideoController( player, @@ -843,8 +832,7 @@ class PlPlayerController with BlockConfigMixin { player.setPlaylistMode(looping); - final Map? filters; - if (Platform.isAndroid) { + if (kDebugMode || Platform.isAndroid) { String audioNormalization = AudioNormalization.getParamFromConfig( Pref.audioNormalization, ); @@ -867,11 +855,9 @@ class PlPlayerController with BlockConfigMixin { AudioNormalization.getParamFromConfig(Pref.fallbackNormalization), ); } - filters = audioNormalization.isEmpty - ? null - : {'lavfi-complex': '"[aid1] $audioNormalization [ao]"'}; - } else { - filters = null; + if (audioNormalization.isNotEmpty) { + extras['lavfi-complex'] = '"[aid1] $audioNormalization [ao]"'; + } } // if (kDebugMode) debugPrint(filters.toString()); @@ -895,7 +881,7 @@ class PlPlayerController with BlockConfigMixin { videoUri, httpHeaders: dataSource.httpHeaders, start: seekTo, - extras: filters, + extras: extras.isEmpty ? null : extras, ), play: false, ); @@ -915,23 +901,22 @@ class PlPlayerController with BlockConfigMixin { SmartDialog.showToast('视频源为空,请重新进入本页面'); return false; } + String? audioUri; if (!isLive) { if (dataSource.audioSource.isNullOrEmpty) { SmartDialog.showToast('音频源为空'); } else { - await (_videoPlayerController!.platform!).setProperty( - 'audio-files', - Platform.isWindows - ? dataSource.audioSource!.replaceAll(';', '\\;') - : dataSource.audioSource!.replaceAll(':', '\\:'), - ); + audioUri = Platform.isWindows + ? dataSource.audioSource!.replaceAll(';', '\\;') + : dataSource.audioSource!.replaceAll(':', '\\:'); } } await _videoPlayerController!.open( Media( dataSource.videoSource!, httpHeaders: dataSource.httpHeaders, - start: position.value, + start: position, + extras: audioUri == null ? null : {'audio-files': '"$audioUri"'}, ), play: true, ); @@ -1038,10 +1023,10 @@ class PlPlayerController with BlockConfigMixin { makeHeartBeat(positionSeconds.value, type: HeartBeatType.completed); }), controllerStream.position.listen((event) { - position.value = event; + position = event; updatePositionSecond(); if (!isSliderMoving.value) { - sliderPosition.value = event; + sliderPosition = event; updateSliderPositionSecond(); } @@ -1069,7 +1054,7 @@ class PlPlayerController with BlockConfigMixin { if (kDebugMode) controllerStream.log.listen(((PlayerLog log) { if (log.level == 'error' || log.level == 'fatal') { - Utils.reportError('${log.prefix}: ${log.text}', null); + Utils.reportError('${log.level}: ${log.prefix}: ${log.text}', null); } else { debugPrint(log.toString()); } @@ -1140,12 +1125,8 @@ class PlPlayerController with BlockConfigMixin { isLive, ); }), - position.listen((Duration event) { - EasyThrottle.throttle( - 'mediaServicePosition', - const Duration(seconds: 1), - () => videoPlayerServiceHandler!.onPositionChange(event), - ); + positionSeconds.listen((int event) { + videoPlayerServiceHandler!.onPositionChange(Duration(seconds: event)); }), ], }; @@ -1167,7 +1148,7 @@ class PlPlayerController with BlockConfigMixin { if (position < Duration.zero) { position = Duration.zero; } - this.position.value = position; + this.position = position; updatePositionSecond(); _heartDuration = position.inSeconds; if (duration.value.inSeconds != 0) { @@ -1194,7 +1175,7 @@ class PlPlayerController with BlockConfigMixin { if (_playerCount == 0) { _timerForSeek?.cancel(); _timerForSeek = null; - } else if (duration.value.inSeconds != 0) { + } else if (duration.value != Duration.zero) { try { await _videoPlayerController?.stream.buffer.first; danmakuController?.clear(); @@ -1288,8 +1269,8 @@ class PlPlayerController with BlockConfigMixin { } /// 调整播放时间 - void onChangedSlider(double v) { - sliderPosition.value = Duration(seconds: v.floor()); + void onChangedSlider(int v) { + sliderPosition = Duration(seconds: v); updateSliderPositionSecond(); } @@ -1305,7 +1286,7 @@ class PlPlayerController with BlockConfigMixin { void onUpdatedSliderProgress(Duration value) { sliderTempPosition.value = value; - sliderPosition.value = value; + sliderPosition = value; updateSliderPositionSecond(); } @@ -1350,14 +1331,6 @@ class PlPlayerController with BlockConfigMixin { }); } - void volumeUpdated() { - showVolumeStatus.value = true; - _timerForShowingVolume?.cancel(); - _timerForShowingVolume = Timer(const Duration(seconds: 1), () { - showVolumeStatus.value = false; - }); - } - /// Toggle Change the videofit accordingly void toggleVideoFit(VideoFitType value) { videoFit.value = value; @@ -1440,7 +1413,7 @@ class PlPlayerController with BlockConfigMixin { bool get _isCompleted => videoPlayerController!.state.completed || - (duration.value - position.value).inMilliseconds <= 50; + (duration.value - position).inMilliseconds <= 50; // 双击播放、暂停 Future onDoubleTapCenter() async { @@ -1464,11 +1437,11 @@ class PlPlayerController with BlockConfigMixin { } void onForward(Duration duration) { - onForwardBackward(position.value + duration); + onForwardBackward(position + duration); } void onBackward(Duration duration) { - onForwardBackward(position.value - duration); + onForwardBackward(position - duration); } void onForwardBackward(Duration duration) { @@ -1604,7 +1577,7 @@ class PlPlayerController with BlockConfigMixin { } // 记录播放记录 - Future makeHeartBeat( + Future? makeHeartBeat( int progress, { HeartBeatType type = HeartBeatType.playing, bool isManual = false, @@ -1615,41 +1588,26 @@ class PlPlayerController with BlockConfigMixin { dynamic seasonId, dynamic pgcType, VideoType? videoType, - }) async { + }) { if (isLive) { - return; + return null; } if (!enableHeart || MineController.anonymity.value || progress == 0) { - return; + return null; } else if (playerStatus.isPaused) { if (!isManual) { - return; + return null; } } bool isComplete = playerStatus.isCompleted || type == HeartBeatType.completed; - if ((durationSeconds.value - position.value).inMilliseconds > 1000) { + if ((duration.value - position).inMilliseconds > 1000) { isComplete = false; } // 播放状态变化时,更新 - if (type == HeartBeatType.status || type == HeartBeatType.completed) { - await VideoHttp.heartBeat( - aid: aid ?? _aid, - bvid: bvid ?? _bvid, - cid: cid ?? this.cid, - progress: isComplete ? -1 : progress, - epid: epid ?? _epid, - seasonId: seasonId ?? _seasonId, - subType: pgcType ?? _pgcType, - videoType: videoType ?? _videoType, - ); - return; - } - // 正常播放时,间隔5秒更新一次 - else if (progress - _heartDuration >= 5) { - _heartDuration = progress; - await VideoHttp.heartBeat( + Future send() { + return VideoHttp.heartBeat( aid: aid ?? _aid, bvid: bvid ?? _bvid, cid: cid ?? this.cid, @@ -1660,6 +1618,23 @@ class PlPlayerController with BlockConfigMixin { videoType: videoType ?? _videoType, ); } + + switch (type) { + case HeartBeatType.playing: + if (progress - _heartDuration >= 5) { + _heartDuration = progress; + return send(); + } + case HeartBeatType.status: + if (progress - _heartDuration >= 2) { + _heartDuration = progress; + return send(); + } + case HeartBeatType.completed: + if (isComplete) progress = -1; + return send(); + } + return null; } void setPlayRepeat(PlayRepeat type) { @@ -1705,7 +1680,6 @@ class PlPlayerController with BlockConfigMixin { Utils.channel.setMethodCallHandler(null); _timer?.cancel(); _timerForSeek?.cancel(); - _timerForShowingVolume?.cancel(); // _position.close(); // _playerEventSubs?.cancel(); // _sliderPosition.close(); @@ -1775,9 +1749,7 @@ class PlPlayerController with BlockConfigMixin { return; } if (videoShot case Success(:final response)) { - if (!showPreview.value) { - showPreview.value = true; - } + showPreview.value = true; previewIndex.value = max( 0, (response.index.where((item) => item <= seconds).length - 2), diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 98e39d9d6..62e83a91e 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -400,7 +400,7 @@ class _PLVideoPlayerState extends State Obx( () => Text( DurationUtils.formatDuration( - plPlayerController.durationSeconds.value.inSeconds, + plPlayerController.duration.value.inSeconds, ), style: const TextStyle( color: Color(0xFFD0D0D0), @@ -1016,15 +1016,12 @@ class _PLVideoPlayerState extends State if (plPlayerController.isLive) return; final int curSliderPosition = - plPlayerController.sliderPosition.value.inMilliseconds; + plPlayerController.sliderPosition.inMilliseconds; final int newPos = (curSliderPosition + (plPlayerController.sliderScale * delta.dx / maxWidth) .round()) - .clamp( - 0, - plPlayerController.duration.value.inMilliseconds, - ); + .clamp(0, plPlayerController.duration.value.inMilliseconds); final Duration result = Duration(milliseconds: newPos); final height = maxHeight * 0.125; if (details.localFocalPoint.dy <= height && @@ -1133,11 +1130,11 @@ class _PLVideoPlayerState extends State if (plPlayerController.isSliderMoving.value) { if (plPlayerController.cancelSeek == true) { plPlayerController.onUpdatedSliderProgress( - plPlayerController.position.value, + plPlayerController.position, ); } else { plPlayerController.seekTo( - plPlayerController.sliderPosition.value, + plPlayerController.sliderPosition, isSeek: false, ); } @@ -1308,15 +1305,12 @@ class _PLVideoPlayerState extends State Offset delta = event.localPanDelta; final int curSliderPosition = - plPlayerController.sliderPosition.value.inMilliseconds; + plPlayerController.sliderPosition.inMilliseconds; final int newPos = (curSliderPosition + (plPlayerController.sliderScale * delta.dx / maxWidth) .round()) - .clamp( - 0, - plPlayerController.duration.value.inMilliseconds, - ); + .clamp(0, plPlayerController.duration.value.inMilliseconds); final Duration result = Duration(milliseconds: newPos); if (plPlayerController.cancelSeek == true) { plPlayerController @@ -1504,10 +1498,7 @@ class _PLVideoPlayerState extends State () { return Text( DurationUtils.formatDuration( - plPlayerController - .durationSeconds - .value - .inSeconds, + plPlayerController.duration.value.inSeconds, ), style: textStyle, ); @@ -1754,7 +1745,7 @@ class _PLVideoPlayerState extends State final int value = plPlayerController.sliderPositionSeconds.value; final int max = - plPlayerController.durationSeconds.value.inSeconds; + plPlayerController.duration.value.inSeconds; final int buffer = plPlayerController.bufferedSeconds.value; return ProgressBar( @@ -1923,8 +1914,7 @@ class _PLVideoPlayerState extends State ), if (plPlayerController.isBuffering.value) Obx(() { - if (plPlayerController.buffered.value == - Duration.zero) { + if (plPlayerController.bufferedSeconds.value == 0) { return const Text( '加载中...', style: TextStyle( @@ -2088,8 +2078,8 @@ class _PLVideoPlayerState extends State } late final segment = Pair( - first: plPlayerController.position.value.inMilliseconds / 1000.0, - second: plPlayerController.position.value.inMilliseconds / 1000.0, + first: plPlayerController.position.inMilliseconds / 1000.0, + second: plPlayerController.position.inMilliseconds / 1000.0, ); Future screenshotWebp() async { @@ -2103,8 +2093,8 @@ class _PLVideoPlayerState extends State final ctr = plPlayerController; final theme = Theme.of(context); - final currentPos = ctr.position.value.inMilliseconds / 1000.0; - final duration = ctr.durationSeconds.value.inMilliseconds / 1000.0; + final currentPos = ctr.position.inMilliseconds / 1000.0; + final duration = ctr.duration.value.inMilliseconds / 1000.0; final model = PostSegmentModel( segment: segment, category: SegmentType.sponsor, diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 195625273..c3cc6128b 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -30,20 +30,20 @@ class BottomControl extends StatelessWidget { controller.onChangedSliderStart(duration.timeStamp); } - void onDragUpdate(ThumbDragDetails duration, int max) { + void onDragUpdate(ThumbDragDetails duration) { if (!controller.isFileSource && controller.showSeekPreview) { controller.updatePreviewIndex(duration.timeStamp.inSeconds); } controller.onUpdatedSliderProgress(duration.timeStamp); } - void onSeek(Duration duration, int max) { + void onSeek(Duration duration) { if (controller.showSeekPreview) { controller.showPreview.value = false; } controller ..onChangedSliderEnd() - ..onChangedSlider(duration.inSeconds.toDouble()) + ..onChangedSlider(duration.inSeconds) ..seekTo(Duration(seconds: duration.inSeconds), isSeek: false); } @@ -72,8 +72,7 @@ class BottomControl extends StatelessWidget { children: [ Obx(() { final int value = controller.sliderPositionSeconds.value; - final int max = - controller.durationSeconds.value.inSeconds; + final int max = controller.duration.value.inSeconds; return ProgressBar( progress: Duration(seconds: value), buffered: Duration( @@ -89,8 +88,8 @@ class BottomControl extends StatelessWidget { thumbRadius: 7, thumbGlowRadius: 25, onDragStart: onDragStart, - onDragUpdate: (e) => onDragUpdate(e, max), - onSeek: (e) => onSeek(e, max), + onDragUpdate: onDragUpdate, + onSeek: onSeek, ); }), if (controller.enableBlock &&