Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 37 additions & 14 deletions lib/pages/video/widgets/player_focus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,22 @@ class PlayerFocus extends StatelessWidget {
if (key == LogicalKeyboardKey.arrowRight) {
if (!plPlayerController.isLive) {
if (event is KeyDownEvent) {
if (hasPlayer && !plPlayerController.longPressStatus.value) {
if (hasPlayer && !plPlayerController.isSeeking.value) {
plPlayerController
..cancelLongPressTimer()
..longPressTimer ??= Timer(
const Duration(milliseconds: 200),
() => plPlayerController
..cancelLongPressTimer()
..setLongPressStatus(true),
() {
plPlayerController.cancelLongPressTimer();
plPlayerController.startSeeking(true);
},
);
}
} else if (event is KeyUpEvent) {
plPlayerController.cancelLongPressTimer();
if (hasPlayer) {
if (plPlayerController.longPressStatus.value) {
plPlayerController.setLongPressStatus(false);
if (plPlayerController.isSeeking.value) {
plPlayerController.endSeeking();
} else {
plPlayerController.onForward(
plPlayerController.fastForBackwardDuration,
Expand All @@ -125,6 +126,36 @@ class PlayerFocus extends StatelessWidget {
return true;
}

if (key == LogicalKeyboardKey.arrowLeft) {
if (!plPlayerController.isLive) {
if (event is KeyDownEvent) {
if (hasPlayer && !plPlayerController.isSeeking.value) {
plPlayerController
..cancelLongPressTimer()
..longPressTimer ??= Timer(
const Duration(milliseconds: 200),
() {
plPlayerController.cancelLongPressTimer();
plPlayerController.startSeeking(false);
},
);
}
} else if (event is KeyUpEvent) {
plPlayerController.cancelLongPressTimer();
if (hasPlayer) {
if (plPlayerController.isSeeking.value) {
plPlayerController.endSeeking();
} else {
plPlayerController.onBackward(
plPlayerController.fastForBackwardDuration,
);
}
}
}
}
return true;
}

if (event is KeyDownEvent) {
final isDigit1 = key == LogicalKeyboardKey.digit1;
if (isDigit1 || key == LogicalKeyboardKey.digit2) {
Expand Down Expand Up @@ -229,14 +260,6 @@ class PlayerFocus extends StatelessWidget {

if (!plPlayerController.isLive) {
switch (key) {
case LogicalKeyboardKey.arrowLeft:
if (hasPlayer) {
plPlayerController.onBackward(
plPlayerController.fastForBackwardDuration,
);
}
return true;

case LogicalKeyboardKey.keyW:
if (HardwareKeyboard.instance.isMetaPressed) {
return true;
Expand Down
87 changes: 87 additions & 0 deletions lib/plugin/pl_player/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ class PlPlayerController {
/// 是否长按倍速
final RxBool longPressStatus = false.obs;

/// 是否在拖拽进度
final RxBool isSeeking = false.obs;

/// 是否在快进
final RxBool isSeekingForward = false.obs;

/// 拖拽进度条展示/隐藏
final RxBool showSeekIndicator = false.obs;

/// 屏幕锁 为true时,关闭控制栏
final RxBool controlsLock = false.obs;

Expand Down Expand Up @@ -645,6 +654,9 @@ class PlPlayerController {
_isVertical = isVertical ?? false;
_aid = aid;
_bvid = bvid;
if (bvid != null && cid != null) {
getVideoShot();
}
this.cid = cid;
_epid = epid;
_seasonId = seasonId;
Expand Down Expand Up @@ -1433,6 +1445,81 @@ class PlPlayerController {
longPressTimer = null;
}

DateTime? _seekStartTime;

Duration _getSeekStep() {
int baseStep = 1;
if (videoShot case Success<VideoShotData> success) {
final data = success.response;
if (data.index.length > 1) {
baseStep = (data.index[1] - data.index[0]).ceil();
}
}

final seekDuration = _seekStartTime != null
? DateTime.now().difference(_seekStartTime!)
: Duration.zero;

int multiplier = 1;
if (seekDuration.inSeconds >= 10) {
multiplier = 10;
} else if (seekDuration.inSeconds >= 5) {
multiplier = 5;
} else if (seekDuration.inSeconds >= 2) {
multiplier = 2;
}

double videoDurationFactor = 1.0;
if (duration.value.inMinutes > 60) {
videoDurationFactor = 4.0;
} else if (duration.value.inMinutes > 30) {
videoDurationFactor = 2.0;
}

return Duration(seconds: (baseStep * multiplier * videoDurationFactor).round());
}

Timer? _seekingTimer;
void startSeeking(bool isForward) {
if (isLive || controlsLock.value || isSeeking.value) return;

_seekStartTime = DateTime.now();
isSeeking.value = true;
isSeekingForward.value = isForward;
showSeekIndicator.value = true;
_videoPlayerController?.pause();
_seekingTimer?.cancel();
_seekingTimer = Timer.periodic(const Duration(milliseconds: 200), (_) {
updateSeekPosition(isForward);
});
}

void updateSeekPosition(bool isForward) {
final step = _getSeekStep();
final newPosition = isForward
? sliderPosition.value + step
: sliderPosition.value - step;

if (newPosition >= Duration.zero && newPosition <= duration.value) {
sliderPosition.value = newPosition;
updateSliderPositionSecond();
}
}

void endSeeking() {
_seekingTimer?.cancel();
_seekStartTime = null;
if (isSeeking.value) {
seekTo(sliderPosition.value).then((_) {
play();
});
}
isSeeking.value = false;
Future.delayed(const Duration(seconds: 1), () {
showSeekIndicator.value = false;
});
}

/// 设置长按倍速状态 live模式下禁用
Future<void> setLongPressStatus(bool val) async {
if (isLive) {
Expand Down
11 changes: 11 additions & 0 deletions lib/plugin/pl_player/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/forward_seek.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/mpv_convert_webp.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/seek_indicator.dart';
import 'package:PiliPlus/utils/duration_utils.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/id_utils.dart';
Expand Down Expand Up @@ -1625,6 +1626,16 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
),
),

/// 拖拽进度条
if (!isLive)
IgnorePointer(
ignoring: true,
child: Align(
alignment: Alignment.center,
child: SeekIndicator(controller: plPlayerController),
),
),

// 头部、底部控制条
Positioned.fill(
top: -1,
Expand Down
48 changes: 48 additions & 0 deletions lib/plugin/pl_player/widgets/seek_indicator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:PiliPlus/utils/duration_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart';

class SeekIndicator extends StatelessWidget {
final PlPlayerController controller;

const SeekIndicator({super.key, required this.controller});

@override
Widget build(BuildContext context) {
return Obx(() {
if (!controller.showSeekIndicator.value) {
return const SizedBox.shrink();
}
return Material(
type: MaterialType.transparency,
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Obx(
() => Icon(
controller.isSeekingForward.value
? Icons.fast_forward
: Icons.fast_rewind,
size: 24.0,
color: Colors.white,
),
),
const SizedBox(height: 8.0),
Obx(() => Text(
'${DurationUtils.formatDuration(controller.sliderPosition.value.inSeconds)} / ${DurationUtils.formatDuration(controller.duration.value.inSeconds)}',
style: const TextStyle(
fontSize: 12.0,
color: Colors.white,
),
)),
],
),
),
);
});
}
}