mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-29 22:00:16 +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) {
|
||||
|
||||
Reference in New Issue
Block a user