mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-28 21:30:18 +08:00
refactor progress bar
Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
@@ -72,38 +72,31 @@ class PlPlayerController with BlockConfigMixin {
|
||||
Player? _videoPlayerController;
|
||||
VideoController? _videoController;
|
||||
|
||||
// 添加一个私有静态变量来保存实例
|
||||
static PlPlayerController? _instance;
|
||||
|
||||
// 流事件 监听播放状态变化
|
||||
// StreamSubscription? _playerEventSubs;
|
||||
final playerStatus = PlPlayerStatus(.playing);
|
||||
|
||||
/// [playerStatus] has a [status] observable
|
||||
final playerStatus = PlPlayerStatus(PlayerStatus.playing);
|
||||
final Rx<DataStatus> dataStatus = Rx(.none);
|
||||
|
||||
///
|
||||
final Rx<DataStatus> dataStatus = Rx(DataStatus.none);
|
||||
Duration? seekToPos;
|
||||
bool hasToasted = false;
|
||||
final RxBool isSeeking = false.obs;
|
||||
|
||||
// bool controlsEnabled = false;
|
||||
final RxInt position = RxInt(0);
|
||||
|
||||
/// 响应数据
|
||||
/// 带有Seconds的变量只在秒数更新时更新,以避免频繁触发重绘
|
||||
// 播放位置
|
||||
Duration position = Duration.zero;
|
||||
final RxInt positionSeconds = 0.obs;
|
||||
int get positionInMilliseconds =>
|
||||
videoPlayerController?.state.position.inMilliseconds ?? 0;
|
||||
|
||||
/// 进度条位置
|
||||
Duration sliderPosition = Duration.zero;
|
||||
final RxInt sliderPositionSeconds = 0.obs;
|
||||
// 展示使用
|
||||
final Rx<Duration> sliderTempPosition = Rx(Duration.zero);
|
||||
final RxInt buffered = RxInt(0);
|
||||
|
||||
/// 视频时长
|
||||
final Rx<Duration> duration = Rx(Duration.zero);
|
||||
final RxInt duration = RxInt(0);
|
||||
|
||||
/// 视频缓冲
|
||||
final Rx<Duration> buffered = Rx(Duration.zero);
|
||||
final RxInt bufferedSeconds = 0.obs;
|
||||
int durationInMilliseconds = 0;
|
||||
|
||||
void updateDuration(Duration value) {
|
||||
duration.value = value.inSeconds;
|
||||
durationInMilliseconds = value.inMilliseconds;
|
||||
}
|
||||
|
||||
int _playerCount = 0;
|
||||
|
||||
@@ -111,44 +104,31 @@ class PlPlayerController with BlockConfigMixin {
|
||||
final RxDouble _playbackSpeed = Pref.playSpeedDefault.obs;
|
||||
late final RxDouble _longPressSpeed = Pref.longPressSpeedDefault.obs;
|
||||
|
||||
/// 音量控制条
|
||||
final RxDouble volume = RxDouble(
|
||||
PlatformUtils.isDesktop ? Pref.desktopVolume : 1.0,
|
||||
);
|
||||
final setSystemBrightness = Pref.setSystemBrightness;
|
||||
|
||||
/// 亮度控制条
|
||||
final RxDouble brightness = (-1.0).obs;
|
||||
|
||||
/// 是否展示控制条
|
||||
final RxBool showControls = false.obs;
|
||||
|
||||
/// 亮度控制条展示/隐藏
|
||||
final RxBool showBrightnessStatus = false.obs;
|
||||
|
||||
/// 是否长按倍速
|
||||
final RxBool longPressStatus = false.obs;
|
||||
|
||||
/// 屏幕锁 为true时,关闭控制栏
|
||||
final RxBool controlsLock = false.obs;
|
||||
|
||||
/// 全屏状态
|
||||
final RxBool isFullScreen = false.obs;
|
||||
// 默认投稿视频格式
|
||||
bool isLive = false;
|
||||
|
||||
bool _isVertical = false;
|
||||
|
||||
/// 视频比例
|
||||
final Rx<VideoFitType> videoFit = Rx(VideoFitType.contain);
|
||||
final Rx<VideoFitType> videoFit = Rx(.contain);
|
||||
|
||||
/// 后台播放
|
||||
late final RxBool continuePlayInBackground =
|
||||
Pref.continuePlayInBackground.obs;
|
||||
|
||||
///
|
||||
final RxBool isSliderMoving = false.obs;
|
||||
|
||||
bool _autoPlay = false;
|
||||
|
||||
// 记录历史记录
|
||||
@@ -168,7 +148,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
late DataSource dataSource;
|
||||
|
||||
Timer? _timer;
|
||||
StreamSubscription<Duration>? _subForSeek;
|
||||
StreamSubscription? _subForSeek;
|
||||
|
||||
Box setting = GStorage.setting;
|
||||
|
||||
@@ -387,8 +367,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
? Pref.sliderDuration / 100
|
||||
: Pref.sliderDuration * 1000;
|
||||
|
||||
num get sliderScale =>
|
||||
isRelative ? duration.value.inMilliseconds * offset : offset;
|
||||
num get sliderScale => isRelative ? durationInMilliseconds * offset : offset;
|
||||
|
||||
// 播放顺序相关
|
||||
late PlayRepeat playRepeat = Pref.playRepeat;
|
||||
@@ -441,27 +420,6 @@ class PlPlayerController with BlockConfigMixin {
|
||||
putSubtitleSettings();
|
||||
}
|
||||
|
||||
void updateSliderPositionSecond() {
|
||||
int newSecond = sliderPosition.inSeconds;
|
||||
if (sliderPositionSeconds.value != newSecond) {
|
||||
sliderPositionSeconds.value = newSecond;
|
||||
}
|
||||
}
|
||||
|
||||
void updatePositionSecond() {
|
||||
int newSecond = position.inSeconds;
|
||||
if (positionSeconds.value != newSecond) {
|
||||
positionSeconds.value = newSecond;
|
||||
}
|
||||
}
|
||||
|
||||
void updateBufferedSecond() {
|
||||
int newSecond = buffered.value.inSeconds;
|
||||
if (bufferedSeconds.value != newSecond) {
|
||||
bufferedSeconds.value = newSecond;
|
||||
}
|
||||
}
|
||||
|
||||
static PlPlayerController? get instance => _instance;
|
||||
|
||||
static bool instanceExists() {
|
||||
@@ -475,7 +433,6 @@ class PlPlayerController with BlockConfigMixin {
|
||||
static PlayCallback? _playCallBack;
|
||||
|
||||
static Future<void>? playIfExists() {
|
||||
// await _instance?.play(repeat: repeat, hideControls: hideControls);
|
||||
return _playCallBack?.call();
|
||||
}
|
||||
|
||||
@@ -688,14 +645,10 @@ class PlPlayerController with BlockConfigMixin {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取视频时长 00:00
|
||||
this.duration.value = duration ?? _videoPlayerController!.state.duration;
|
||||
position = buffered.value = sliderPosition = seekTo ?? Duration.zero;
|
||||
updatePositionSecond();
|
||||
updateSliderPositionSecond();
|
||||
updateBufferedSecond();
|
||||
// 数据加载完成
|
||||
dataStatus.value = DataStatus.loaded;
|
||||
updateDuration(duration ?? _videoPlayerController!.state.duration);
|
||||
position.value = buffered.value = seekTo?.inSeconds ?? 0;
|
||||
|
||||
dataStatus.value = .loaded;
|
||||
|
||||
if (autoFullScreenFlag && autoEnterFullScreen) {
|
||||
triggerFullScreen(status: true);
|
||||
@@ -821,10 +774,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
Volume? volume,
|
||||
) async {
|
||||
isBuffering.value = false;
|
||||
buffered.value = Duration.zero;
|
||||
_heartDuration = 0;
|
||||
position = Duration.zero;
|
||||
// 初始化时清空弹幕,防止上次重叠
|
||||
danmakuController?.clear();
|
||||
|
||||
var player = _videoPlayerController;
|
||||
@@ -906,7 +856,10 @@ class PlPlayerController with BlockConfigMixin {
|
||||
return null;
|
||||
}
|
||||
if (_videoPlayerController case final ctr? when (ctr.current.isNotEmpty)) {
|
||||
return ctr.open(ctr.current.last.copyWith(start: position), play: true);
|
||||
return ctr.open(
|
||||
ctr.current.last.copyWith(start: ctr.state.position),
|
||||
play: true,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -948,9 +901,10 @@ class PlPlayerController with BlockConfigMixin {
|
||||
assert(_subscriptions == null);
|
||||
final stream = player.stream;
|
||||
_subscriptions = [
|
||||
stream.playing.listen((event) {
|
||||
WakelockPlus.toggle(enable: event);
|
||||
if (event) {
|
||||
/// playing
|
||||
stream.playing.listen((bool playing) {
|
||||
WakelockPlus.toggle(enable: playing);
|
||||
if (playing) {
|
||||
if (_isAutoEnterPip) {
|
||||
if (_isCurrVideoPage) {
|
||||
enterPip(autoEnter: true);
|
||||
@@ -958,64 +912,68 @@ class PlPlayerController with BlockConfigMixin {
|
||||
_disableAutoEnterPip();
|
||||
}
|
||||
}
|
||||
playerStatus.value = PlayerStatus.playing;
|
||||
playerStatus.value = .playing;
|
||||
} else {
|
||||
_disableAutoEnterPip();
|
||||
playerStatus.value = PlayerStatus.paused;
|
||||
playerStatus.value = .paused;
|
||||
}
|
||||
|
||||
videoPlayerServiceHandler?.onStatusChange(
|
||||
playerStatus.value,
|
||||
isBuffering.value,
|
||||
isLive,
|
||||
);
|
||||
|
||||
/// 触发回调事件
|
||||
for (final element in _statusListeners) {
|
||||
element(event ? PlayerStatus.playing : PlayerStatus.paused);
|
||||
element(playing ? .playing : .paused);
|
||||
}
|
||||
if (videoPlayerController!.state.position.inSeconds != 0) {
|
||||
makeHeartBeat(positionSeconds.value, type: HeartBeatType.status);
|
||||
|
||||
final seconds = videoPlayerController!.state.position.inSeconds;
|
||||
if (seconds != 0) {
|
||||
makeHeartBeat(seconds, type: .status);
|
||||
}
|
||||
}),
|
||||
stream.completed.listen((event) {
|
||||
if (event) {
|
||||
playerStatus.value = PlayerStatus.completed;
|
||||
|
||||
/// 触发回调事件
|
||||
///completed
|
||||
stream.completed.listen((bool completed) {
|
||||
if (completed) {
|
||||
playerStatus.value = .completed;
|
||||
|
||||
for (final element in _statusListeners) {
|
||||
element(PlayerStatus.completed);
|
||||
element(.completed);
|
||||
}
|
||||
} else {
|
||||
// playerStatus.value = PlayerStatus.playing;
|
||||
|
||||
makeHeartBeat(-1, type: .completed);
|
||||
}
|
||||
makeHeartBeat(positionSeconds.value, type: HeartBeatType.completed);
|
||||
}),
|
||||
stream.position.listen((event) {
|
||||
position = event;
|
||||
updatePositionSecond();
|
||||
if (!isSliderMoving.value) {
|
||||
sliderPosition = event;
|
||||
updateSliderPositionSecond();
|
||||
|
||||
/// position
|
||||
stream.position.listen((Duration position) {
|
||||
final posInSeconds = position.inSeconds;
|
||||
|
||||
if (posInSeconds != this.position.value) {
|
||||
if (!isSeeking.value) {
|
||||
this.position.value = posInSeconds;
|
||||
}
|
||||
|
||||
videoPlayerServiceHandler?.onPositionChange(position);
|
||||
}
|
||||
|
||||
/// 触发回调事件
|
||||
for (final element in _positionListeners) {
|
||||
element(event);
|
||||
element(position);
|
||||
}
|
||||
makeHeartBeat(event.inSeconds);
|
||||
|
||||
makeHeartBeat(posInSeconds);
|
||||
}),
|
||||
stream.duration.listen((Duration event) {
|
||||
duration.value = event;
|
||||
stream.duration.listen(updateDuration),
|
||||
stream.buffer.listen((Duration buffer) {
|
||||
buffered.value = buffer.inSeconds;
|
||||
}),
|
||||
stream.buffer.listen((Duration event) {
|
||||
buffered.value = event;
|
||||
updateBufferedSecond();
|
||||
}),
|
||||
stream.buffering.listen((bool event) {
|
||||
isBuffering.value = event;
|
||||
stream.buffering.listen((bool buffering) {
|
||||
isBuffering.value = buffering;
|
||||
videoPlayerServiceHandler?.onStatusChange(
|
||||
playerStatus.value,
|
||||
event,
|
||||
buffering,
|
||||
isLive,
|
||||
);
|
||||
}),
|
||||
@@ -1056,7 +1014,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
// if (kDebugMode) {
|
||||
// debugPrint("_buffered.value: ${_buffered.value}");
|
||||
// }
|
||||
if (isBuffering.value && buffered.value == Duration.zero) {
|
||||
if (isBuffering.value && buffered.value == 0) {
|
||||
SmartDialog.showToast(
|
||||
'视频链接打开失败,重试中',
|
||||
displayTime: const Duration(milliseconds: 500),
|
||||
@@ -1079,16 +1037,6 @@ class PlPlayerController with BlockConfigMixin {
|
||||
// SmartDialog.showToast('视频加载错误, $event');
|
||||
}
|
||||
}),
|
||||
// controllerStream.volume.listen((event) {
|
||||
// if (!mute.value && _volumeBeforeMute != event) {
|
||||
// _volumeBeforeMute = event / 100;
|
||||
// }
|
||||
// }),
|
||||
// 媒体通知监听
|
||||
if (videoPlayerServiceHandler != null)
|
||||
positionSeconds.listen((int event) {
|
||||
videoPlayerServiceHandler!.onPositionChange(Duration(seconds: event));
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1108,17 +1056,12 @@ class PlPlayerController with BlockConfigMixin {
|
||||
|
||||
/// 跳转至指定位置
|
||||
Future<void> seekTo(Duration position, {bool isSeek = true}) async {
|
||||
// if (position >= duration.value) {
|
||||
// position = duration.value - const Duration(milliseconds: 100);
|
||||
// }
|
||||
if (_playerCount == 0) {
|
||||
return;
|
||||
}
|
||||
if (position < Duration.zero) {
|
||||
position = Duration.zero;
|
||||
}
|
||||
this.position = position;
|
||||
updatePositionSecond();
|
||||
_heartDuration = position.inSeconds;
|
||||
|
||||
Future<void> seek() async {
|
||||
@@ -1134,7 +1077,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
}
|
||||
}
|
||||
|
||||
if (duration.value != Duration.zero) {
|
||||
if (duration.value != 0) {
|
||||
seek();
|
||||
} else {
|
||||
// if (kDebugMode) debugPrint('seek duration else');
|
||||
@@ -1214,42 +1157,22 @@ class PlPlayerController with BlockConfigMixin {
|
||||
void hideTaskControls() {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(showControlDuration, () {
|
||||
if (!isSliderMoving.value && !tripling) {
|
||||
if (!isSeeking.value && !tripling) {
|
||||
controls = false;
|
||||
}
|
||||
_timer = null;
|
||||
});
|
||||
}
|
||||
|
||||
/// 调整播放时间
|
||||
void onChangedSlider(int v) {
|
||||
sliderPosition = Duration(seconds: v);
|
||||
updateSliderPositionSecond();
|
||||
}
|
||||
|
||||
void onChangedSliderStart([Duration? value]) {
|
||||
if (value != null) {
|
||||
sliderTempPosition.value = value;
|
||||
}
|
||||
isSliderMoving.value = true;
|
||||
}
|
||||
|
||||
bool? cancelSeek;
|
||||
bool? hasToast;
|
||||
|
||||
void onUpdatedSliderProgress(Duration value) {
|
||||
sliderTempPosition.value = value;
|
||||
sliderPosition = value;
|
||||
updateSliderPositionSecond();
|
||||
}
|
||||
|
||||
void onChangedSliderEnd() {
|
||||
if (cancelSeek != true) {
|
||||
void onSeekEnd() {
|
||||
if (seekToPos != null) {
|
||||
feedBack();
|
||||
}
|
||||
cancelSeek = null;
|
||||
hasToast = null;
|
||||
isSliderMoving.value = false;
|
||||
if (showSeekPreview) {
|
||||
showPreview.value = false;
|
||||
}
|
||||
hasToasted = false;
|
||||
isSeeking.value = false;
|
||||
hideTaskControls();
|
||||
}
|
||||
|
||||
@@ -1350,13 +1273,9 @@ class PlPlayerController with BlockConfigMixin {
|
||||
}
|
||||
}
|
||||
|
||||
bool get _isCompleted =>
|
||||
videoPlayerController!.state.completed ||
|
||||
(duration.value - position).inMilliseconds <= 50;
|
||||
|
||||
// 双击播放、暂停
|
||||
Future<void> onDoubleTapCenter() async {
|
||||
if (!isLive && _isCompleted) {
|
||||
if (!isLive && videoPlayerController!.state.completed) {
|
||||
await videoPlayerController!.seek(Duration.zero);
|
||||
videoPlayerController!.play();
|
||||
} else {
|
||||
@@ -1376,11 +1295,11 @@ class PlPlayerController with BlockConfigMixin {
|
||||
}
|
||||
|
||||
void onForward(Duration duration) {
|
||||
onForwardBackward(position + duration);
|
||||
onForwardBackward(videoPlayerController!.state.position + duration);
|
||||
}
|
||||
|
||||
void onBackward(Duration duration) {
|
||||
onForwardBackward(position - duration);
|
||||
onForwardBackward(videoPlayerController!.state.position - duration);
|
||||
}
|
||||
|
||||
void onForwardBackward(Duration duration) {
|
||||
@@ -1568,7 +1487,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
}
|
||||
case .completed:
|
||||
if (playerStatus.isCompleted &&
|
||||
(duration.value - position).inMilliseconds <= 1000) {
|
||||
(durationInMilliseconds - positionInMilliseconds) <= 1000) {
|
||||
progress = -1;
|
||||
}
|
||||
return send();
|
||||
@@ -1734,7 +1653,7 @@ class PlPlayerController with BlockConfigMixin {
|
||||
Future<void> takeScreenshot() async {
|
||||
SmartDialog.showToast('截图中');
|
||||
final time = DurationUtils.formatDuration(
|
||||
position.inMilliseconds / 1000,
|
||||
positionInMilliseconds / 1000,
|
||||
).replaceAll(':', '-');
|
||||
final image = await videoPlayerController?.screenshot();
|
||||
if (image != null) {
|
||||
|
||||
@@ -447,10 +447,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
BottomControlType.time => Obx(
|
||||
() => _VideoTime(
|
||||
position: DurationUtils.formatDuration(
|
||||
plPlayerController.positionSeconds.value,
|
||||
plPlayerController.position.value,
|
||||
),
|
||||
duration: DurationUtils.formatDuration(
|
||||
plPlayerController.duration.value.inSeconds,
|
||||
plPlayerController.duration.value,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -961,6 +961,42 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
showRestoreScaleBtn.value = scale != 1.0;
|
||||
}
|
||||
|
||||
void _onHorizontalDragStart() {
|
||||
plPlayerController.isSeeking.value = true;
|
||||
}
|
||||
|
||||
void _onHorizontalDragUpdate(double dx) {
|
||||
final curPos =
|
||||
plPlayerController.seekToPos?.inMilliseconds ??
|
||||
plPlayerController.position.value * 1000;
|
||||
final posDelta = (plPlayerController.sliderScale * dx / maxWidth).round();
|
||||
final newPos = (curPos + posDelta).clamp(
|
||||
0,
|
||||
plPlayerController.durationInMilliseconds,
|
||||
);
|
||||
final seconds = newPos ~/ 1000;
|
||||
plPlayerController
|
||||
..seekToPos = Duration(milliseconds: newPos)
|
||||
..position.value = seconds;
|
||||
if (!plPlayerController.isFileSource &&
|
||||
plPlayerController.showSeekPreview) {
|
||||
plPlayerController.updatePreviewIndex(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
void _onHorizontalDragEnd() {
|
||||
plPlayerController.onSeekEnd();
|
||||
if (plPlayerController.seekToPos case final seekToPos?) {
|
||||
plPlayerController
|
||||
..seekTo(seekToPos, isSeek: false)
|
||||
..seekToPos = null;
|
||||
} else {
|
||||
plPlayerController.position.value =
|
||||
plPlayerController.videoPlayerController?.state.position.inSeconds ??
|
||||
0;
|
||||
}
|
||||
}
|
||||
|
||||
void _onPanUpdate(ScaleUpdateDetails details) {
|
||||
if (_gestureType == null) {
|
||||
final cumulativeDelta = details.localFocalPoint - _initialFocalPoint!;
|
||||
@@ -968,6 +1004,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
final dx = cumulativeDelta.dx.abs();
|
||||
final dy = cumulativeDelta.dy.abs();
|
||||
if (dx > 3 * dy) {
|
||||
_onHorizontalDragStart();
|
||||
_gestureType = .horizontal;
|
||||
} else if (dy > 3 * dx) {
|
||||
if (!plPlayerController.enableSlideVolumeBrightness &&
|
||||
@@ -1010,22 +1047,17 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
// live模式下禁用
|
||||
if (plPlayerController.isLive) return;
|
||||
|
||||
final int curSliderPosition =
|
||||
plPlayerController.sliderPosition.inMilliseconds;
|
||||
final int newPos =
|
||||
(curSliderPosition +
|
||||
(plPlayerController.sliderScale * delta.dx / maxWidth)
|
||||
.round())
|
||||
.clamp(0, plPlayerController.duration.value.inMilliseconds);
|
||||
final Duration result = Duration(milliseconds: newPos);
|
||||
final height = maxHeight * 0.125;
|
||||
if (details.localFocalPoint.dy <= height &&
|
||||
(details.localFocalPoint.dx >= maxWidth * 0.875 ||
|
||||
details.localFocalPoint.dx <= maxWidth * 0.125)) {
|
||||
plPlayerController.cancelSeek = true;
|
||||
plPlayerController.showPreview.value = false;
|
||||
if (plPlayerController.hasToast != true) {
|
||||
plPlayerController.hasToast = true;
|
||||
if (!plPlayerController.hasToasted) {
|
||||
plPlayerController
|
||||
..seekToPos = null
|
||||
..hasToasted = true;
|
||||
if (plPlayerController.showSeekPreview) {
|
||||
plPlayerController.showPreview.value = false;
|
||||
}
|
||||
SmartDialog.showAttach(
|
||||
targetContext: context,
|
||||
alignment: Alignment.center,
|
||||
@@ -1046,21 +1078,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (plPlayerController.cancelSeek == true) {
|
||||
plPlayerController
|
||||
..cancelSeek = null
|
||||
..hasToast = null;
|
||||
}
|
||||
}
|
||||
plPlayerController
|
||||
..onUpdatedSliderProgress(result)
|
||||
..onChangedSliderStart();
|
||||
if (!plPlayerController.isFileSource &&
|
||||
plPlayerController.showSeekPreview &&
|
||||
plPlayerController.cancelSeek != true) {
|
||||
plPlayerController.updatePreviewIndex(newPos ~/ 1000);
|
||||
return;
|
||||
} else if (plPlayerController.hasToasted) {
|
||||
plPlayerController.hasToasted = false;
|
||||
}
|
||||
|
||||
_onHorizontalDragUpdate(delta.dx);
|
||||
} else if (_gestureType == .left) {
|
||||
// 左边区域 👈
|
||||
final double level = maxHeight * 3;
|
||||
@@ -1110,21 +1133,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
|
||||
void _onPanEnd(ScaleEndDetails details) {
|
||||
if (plPlayerController.showSeekPreview) {
|
||||
plPlayerController.showPreview.value = false;
|
||||
}
|
||||
if (plPlayerController.isSliderMoving.value) {
|
||||
if (plPlayerController.cancelSeek == true) {
|
||||
plPlayerController.onUpdatedSliderProgress(
|
||||
plPlayerController.position,
|
||||
);
|
||||
} else {
|
||||
plPlayerController.seekTo(
|
||||
plPlayerController.sliderPosition,
|
||||
isSeek: false,
|
||||
);
|
||||
}
|
||||
plPlayerController.onChangedSliderEnd();
|
||||
if (_gestureType == .horizontal) {
|
||||
_onHorizontalDragEnd();
|
||||
}
|
||||
_initialFocalPoint = null;
|
||||
_gestureType = null;
|
||||
@@ -1274,6 +1284,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
final dx = pan.dx.abs();
|
||||
final dy = pan.dy.abs();
|
||||
if (dx > 3 * dy) {
|
||||
_onHorizontalDragStart();
|
||||
_gestureType = .horizontal;
|
||||
} else if (dy > 3 * dx) {
|
||||
_gestureType = .right;
|
||||
@@ -1284,28 +1295,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
if (_gestureType == .horizontal) {
|
||||
if (plPlayerController.isLive) return;
|
||||
|
||||
final delta = event.localPanDelta;
|
||||
final int curSliderPosition =
|
||||
plPlayerController.sliderPosition.inMilliseconds;
|
||||
final int newPos =
|
||||
(curSliderPosition +
|
||||
(plPlayerController.sliderScale * delta.dx / maxWidth)
|
||||
.round())
|
||||
.clamp(0, plPlayerController.duration.value.inMilliseconds);
|
||||
final Duration result = Duration(milliseconds: newPos);
|
||||
if (plPlayerController.cancelSeek == true) {
|
||||
plPlayerController
|
||||
..cancelSeek = null
|
||||
..hasToast = null;
|
||||
}
|
||||
plPlayerController
|
||||
..onUpdatedSliderProgress(result)
|
||||
..onChangedSliderStart();
|
||||
if (!plPlayerController.isFileSource &&
|
||||
plPlayerController.showSeekPreview &&
|
||||
plPlayerController.cancelSeek != true) {
|
||||
plPlayerController.updatePreviewIndex(newPos ~/ 1000);
|
||||
}
|
||||
_onHorizontalDragUpdate(event.localPanDelta.dx);
|
||||
} else if (_gestureType == .right) {
|
||||
if (!plPlayerController.enableSlideVolumeBrightness) {
|
||||
return;
|
||||
@@ -1328,11 +1318,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
|
||||
void _onPointerPanZoomEnd(PointerPanZoomEndEvent event) {
|
||||
plPlayerController.showPreview.value = false;
|
||||
if (plPlayerController.isSliderMoving.value) {
|
||||
plPlayerController
|
||||
..seekTo(plPlayerController.sliderPosition, isSeek: false)
|
||||
..onChangedSliderEnd();
|
||||
if (_gestureType == .horizontal) {
|
||||
_onHorizontalDragEnd();
|
||||
}
|
||||
_gestureType = null;
|
||||
}
|
||||
@@ -1455,9 +1442,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
child: Obx(
|
||||
() => AnimatedOpacity(
|
||||
curve: Curves.easeInOut,
|
||||
opacity: plPlayerController.isSliderMoving.value
|
||||
? 1.0
|
||||
: 0.0,
|
||||
opacity: plPlayerController.isSeeking.value ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
@@ -1473,27 +1458,22 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Obx(() {
|
||||
return Text(
|
||||
Obx(
|
||||
() => Text(
|
||||
DurationUtils.formatDuration(
|
||||
plPlayerController
|
||||
.sliderTempPosition
|
||||
.value
|
||||
.inSeconds,
|
||||
plPlayerController.position.value,
|
||||
),
|
||||
style: textStyle,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
const Text('/', style: textStyle),
|
||||
Obx(
|
||||
() {
|
||||
return Text(
|
||||
DurationUtils.formatDuration(
|
||||
plPlayerController.duration.value.inSeconds,
|
||||
),
|
||||
style: textStyle,
|
||||
);
|
||||
},
|
||||
() => Text(
|
||||
DurationUtils.formatDuration(
|
||||
plPlayerController.duration.value,
|
||||
),
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -1727,20 +1707,18 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
case .alwaysShow:
|
||||
offstage = showControls;
|
||||
case .alwaysHide:
|
||||
if (!plPlayerController.isSliderMoving.value) {
|
||||
if (!plPlayerController.isSeeking.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
offstage = showControls;
|
||||
case .onlyShowFullScreen:
|
||||
offstage =
|
||||
showControls ||
|
||||
(!isFullScreen &&
|
||||
!plPlayerController.isSliderMoving.value);
|
||||
(!isFullScreen && !plPlayerController.isSeeking.value);
|
||||
case .onlyHideFullScreen:
|
||||
offstage =
|
||||
showControls ||
|
||||
(isFullScreen &&
|
||||
!plPlayerController.isSliderMoving.value);
|
||||
(isFullScreen && !plPlayerController.isSeeking.value);
|
||||
}
|
||||
return Offstage(
|
||||
offstage: offstage,
|
||||
@@ -1748,17 +1726,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Obx(() {
|
||||
final int value =
|
||||
plPlayerController.sliderPositionSeconds.value;
|
||||
final int max =
|
||||
plPlayerController.duration.value.inSeconds;
|
||||
final int buffer =
|
||||
plPlayerController.bufferedSeconds.value;
|
||||
return ProgressBar(
|
||||
progress: Duration(seconds: value),
|
||||
buffered: Duration(seconds: buffer),
|
||||
total: Duration(seconds: max),
|
||||
Obx(
|
||||
() => ProgressBar(
|
||||
progress: plPlayerController.position.value,
|
||||
buffered: plPlayerController.buffered.value,
|
||||
total: plPlayerController.duration.value,
|
||||
progressBarColor: primary,
|
||||
baseBarColor: const Color(0x33FFFFFF),
|
||||
bufferedBarColor: bufferedBarColor,
|
||||
@@ -1766,8 +1738,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
thumbGlowColor: thumbGlowColor,
|
||||
barHeight: 3.5,
|
||||
thumbRadius: 2.5,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
if (plPlayerController.enableBlock &&
|
||||
videoDetailController.segmentProgressList.isNotEmpty)
|
||||
Positioned(
|
||||
@@ -1929,7 +1901,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
),
|
||||
if (plPlayerController.isBuffering.value)
|
||||
Obx(() {
|
||||
if (plPlayerController.bufferedSeconds.value == 0) {
|
||||
final buffered = plPlayerController.buffered.value;
|
||||
if (buffered == 0) {
|
||||
return const Text(
|
||||
'加载中...',
|
||||
style: TextStyle(
|
||||
@@ -1938,10 +1911,8 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
),
|
||||
);
|
||||
}
|
||||
String bufferStr = plPlayerController.buffered
|
||||
.toString();
|
||||
return Text(
|
||||
bufferStr.substring(0, bufferStr.length - 3),
|
||||
DurationUtils.formatDuration(buffered),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
@@ -2087,11 +2058,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
);
|
||||
}
|
||||
|
||||
late final segment = Pair(
|
||||
first: plPlayerController.position.inMilliseconds / 1000.0,
|
||||
second: plPlayerController.position.inMilliseconds / 1000.0,
|
||||
);
|
||||
|
||||
Future<void> screenshotWebp() async {
|
||||
final videoInfo = videoDetailController.data;
|
||||
final ids = videoInfo.dash!.video!.map((i) => i.id!).toSet();
|
||||
@@ -2103,8 +2069,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
final ctr = plPlayerController;
|
||||
final theme = Theme.of(context);
|
||||
final currentPos = ctr.position.inMilliseconds / 1000.0;
|
||||
final duration = ctr.duration.value.inMilliseconds / 1000.0;
|
||||
final currentPos = ctr.positionInMilliseconds / 1000.0;
|
||||
final duration = ctr.durationInMilliseconds / 1000.0;
|
||||
final segment = Pair(first: currentPos, second: currentPos);
|
||||
final model = PostSegmentModel(
|
||||
segment: segment,
|
||||
category: SegmentType.sponsor,
|
||||
|
||||
@@ -27,24 +27,22 @@ class BottomControl extends StatelessWidget {
|
||||
|
||||
void onDragStart(ThumbDragDetails duration) {
|
||||
feedBack();
|
||||
controller.onChangedSliderStart(duration.timeStamp);
|
||||
controller
|
||||
..position.value = duration.seconds
|
||||
..isSeeking.value = true;
|
||||
}
|
||||
|
||||
void onDragUpdate(ThumbDragDetails duration) {
|
||||
if (!controller.isFileSource && controller.showSeekPreview) {
|
||||
controller.updatePreviewIndex(duration.timeStamp.inSeconds);
|
||||
controller.updatePreviewIndex(duration.seconds);
|
||||
}
|
||||
controller.onUpdatedSliderProgress(duration.timeStamp);
|
||||
controller.position.value = duration.seconds;
|
||||
}
|
||||
|
||||
void onSeek(Duration duration) {
|
||||
if (controller.showSeekPreview) {
|
||||
controller.showPreview.value = false;
|
||||
}
|
||||
void onSeek(int milliseconds) {
|
||||
controller
|
||||
..onChangedSliderEnd()
|
||||
..onChangedSlider(duration.inSeconds)
|
||||
..seekTo(Duration(seconds: duration.inSeconds), isSeek: false);
|
||||
..onSeekEnd()
|
||||
..seekTo(Duration(milliseconds: milliseconds), isSeek: false);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -70,15 +68,11 @@ class BottomControl extends StatelessWidget {
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Obx(() {
|
||||
final int value = controller.sliderPositionSeconds.value;
|
||||
final int max = controller.duration.value.inSeconds;
|
||||
return ProgressBar(
|
||||
progress: Duration(seconds: value),
|
||||
buffered: Duration(
|
||||
seconds: controller.bufferedSeconds.value,
|
||||
),
|
||||
total: Duration(seconds: max),
|
||||
Obx(
|
||||
() => ProgressBar(
|
||||
progress: controller.position.value,
|
||||
buffered: controller.buffered.value,
|
||||
total: controller.duration.value,
|
||||
progressBarColor: primary,
|
||||
baseBarColor: const Color(0x33FFFFFF),
|
||||
bufferedBarColor: bufferedBarColor,
|
||||
@@ -90,8 +84,8 @@ class BottomControl extends StatelessWidget {
|
||||
onDragStart: onDragStart,
|
||||
onDragUpdate: onDragUpdate,
|
||||
onSeek: onSeek,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
if (controller.enableBlock &&
|
||||
videoDetailController.segmentProgressList.isNotEmpty)
|
||||
Positioned(
|
||||
|
||||
Reference in New Issue
Block a user