mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-24 11:06:51 +08:00
@@ -343,8 +343,9 @@ class PlPlayerController {
|
||||
late final fullScreenGestureReverse = Pref.fullScreenGestureReverse;
|
||||
|
||||
late final isRelative = Pref.useRelativeSlide;
|
||||
late final offset =
|
||||
isRelative ? Pref.sliderDuration / 100 : Pref.sliderDuration * 1000;
|
||||
late final offset = isRelative
|
||||
? Pref.sliderDuration / 100
|
||||
: Pref.sliderDuration * 1000;
|
||||
|
||||
num get sliderScale =>
|
||||
isRelative ? duration.value.inMilliseconds * offset : offset;
|
||||
@@ -353,17 +354,17 @@ class PlPlayerController {
|
||||
late PlayRepeat playRepeat = PlayRepeat.values[Pref.playRepeat];
|
||||
|
||||
TextStyle get subTitleStyle => TextStyle(
|
||||
height: 1.5,
|
||||
fontSize:
|
||||
16 * (isFullScreen.value ? subtitleFontScaleFS : subtitleFontScale),
|
||||
letterSpacing: 0.1,
|
||||
wordSpacing: 0.1,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.values[subtitleFontWeight],
|
||||
backgroundColor: subtitleBgOpaticy == 0
|
||||
? null
|
||||
: Colors.black.withValues(alpha: subtitleBgOpaticy),
|
||||
);
|
||||
height: 1.5,
|
||||
fontSize:
|
||||
16 * (isFullScreen.value ? subtitleFontScaleFS : subtitleFontScale),
|
||||
letterSpacing: 0.1,
|
||||
wordSpacing: 0.1,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.values[subtitleFontWeight],
|
||||
backgroundColor: subtitleBgOpaticy == 0
|
||||
? null
|
||||
: Colors.black.withValues(alpha: subtitleBgOpaticy),
|
||||
);
|
||||
|
||||
SubtitleViewConfiguration get subtitleViewConfiguration =>
|
||||
SubtitleViewConfiguration(
|
||||
@@ -381,8 +382,8 @@ class PlPlayerController {
|
||||
|
||||
void updateSubtitleStyle() {
|
||||
getPlayerKey?.call().currentState?.update(
|
||||
subtitleViewConfiguration: subtitleViewConfiguration,
|
||||
);
|
||||
subtitleViewConfiguration: subtitleViewConfiguration,
|
||||
);
|
||||
}
|
||||
|
||||
void onUpdatePadding(EdgeInsets padding) {
|
||||
@@ -439,8 +440,10 @@ class PlPlayerController {
|
||||
return _instance?.playerStatus.status.value;
|
||||
}
|
||||
|
||||
static Future<void> pauseIfExists(
|
||||
{bool notify = true, bool isInterrupt = false}) async {
|
||||
static Future<void> pauseIfExists({
|
||||
bool notify = true,
|
||||
bool isInterrupt = false,
|
||||
}) async {
|
||||
if (_instance?.playerStatus.status.value == PlayerStatus.playing) {
|
||||
await _instance?.pause(notify: notify, isInterrupt: isInterrupt);
|
||||
}
|
||||
@@ -454,8 +457,10 @@ class PlPlayerController {
|
||||
return _instance?.volume.value;
|
||||
}
|
||||
|
||||
static Future<void> setVolumeIfExists(double volumeNew,
|
||||
{bool videoPlayerVolume = false}) async {
|
||||
static Future<void> setVolumeIfExists(
|
||||
double volumeNew, {
|
||||
bool videoPlayerVolume = false,
|
||||
}) async {
|
||||
await _instance?.setVolume(volumeNew, videoPlayerVolume: videoPlayerVolume);
|
||||
}
|
||||
|
||||
@@ -561,13 +566,16 @@ class PlPlayerController {
|
||||
return;
|
||||
}
|
||||
// 配置Player 音轨、字幕等等
|
||||
_videoPlayerController =
|
||||
await _createVideoController(dataSource, _looping, seekTo);
|
||||
_videoPlayerController = await _createVideoController(
|
||||
dataSource,
|
||||
_looping,
|
||||
seekTo,
|
||||
);
|
||||
callback?.call();
|
||||
// 获取视频时长 00:00
|
||||
_duration.value = duration ?? _videoPlayerController!.state.duration;
|
||||
_position.value =
|
||||
_buffered.value = _sliderPosition.value = seekTo ?? Duration.zero;
|
||||
_position.value = _buffered.value = _sliderPosition.value =
|
||||
seekTo ?? Duration.zero;
|
||||
updateDurationSecond();
|
||||
updatePositionSecond();
|
||||
updateSliderPositionSecond();
|
||||
@@ -603,8 +611,10 @@ class PlPlayerController {
|
||||
await shadersDirectory!.create(recursive: true);
|
||||
}
|
||||
|
||||
final shaderFiles = manifestMap.keys.where((String key) =>
|
||||
key.startsWith('assets/shaders/') && key.endsWith('.glsl'));
|
||||
final shaderFiles = manifestMap.keys.where(
|
||||
(String key) =>
|
||||
key.startsWith('assets/shaders/') && key.endsWith('.glsl'),
|
||||
);
|
||||
|
||||
// int copiedFilesCount = 0;
|
||||
|
||||
@@ -681,17 +691,18 @@ class PlPlayerController {
|
||||
_position.value = Duration.zero;
|
||||
// 初始化时清空弹幕,防止上次重叠
|
||||
danmakuController?.clear();
|
||||
Player player = _videoPlayerController ??
|
||||
Player player =
|
||||
_videoPlayerController ??
|
||||
Player(
|
||||
configuration: PlayerConfiguration(
|
||||
// 默认缓冲 4M 大小
|
||||
bufferSize: Pref.expandBuffer
|
||||
? (videoType.value == 'live'
|
||||
? 64 * 1024 * 1024
|
||||
: 32 * 1024 * 1024)
|
||||
? 64 * 1024 * 1024
|
||||
: 32 * 1024 * 1024)
|
||||
: (videoType.value == 'live'
|
||||
? 16 * 1024 * 1024
|
||||
: 4 * 1024 * 1024),
|
||||
? 16 * 1024 * 1024
|
||||
: 4 * 1024 * 1024),
|
||||
),
|
||||
);
|
||||
var pp = player.platform as NativePlayer;
|
||||
@@ -712,8 +723,9 @@ class PlPlayerController {
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
await pp.setProperty("volume-max", "100");
|
||||
String ao =
|
||||
Pref.useOpenSLES ? "opensles,audiotrack" : "audiotrack,opensles";
|
||||
String ao = Pref.useOpenSLES
|
||||
? "opensles,audiotrack"
|
||||
: "audiotrack,opensles";
|
||||
await pp.setProperty("ao", ao);
|
||||
}
|
||||
// video-sync=display-resample
|
||||
@@ -770,8 +782,11 @@ class PlPlayerController {
|
||||
);
|
||||
} else {
|
||||
await player.open(
|
||||
Media(dataSource.videoSource!,
|
||||
httpHeaders: dataSource.httpHeaders, start: seekTo),
|
||||
Media(
|
||||
dataSource.videoSource!,
|
||||
httpHeaders: dataSource.httpHeaders,
|
||||
start: seekTo,
|
||||
),
|
||||
play: false,
|
||||
);
|
||||
}
|
||||
@@ -937,22 +952,28 @@ class PlPlayerController {
|
||||
//tcp: ffurl_read returned 0xdfb9b0bb
|
||||
//tcp: ffurl_read returned 0xffffff99
|
||||
event.startsWith('tcp: ffurl_read returned ')) {
|
||||
EasyThrottle.throttle('videoPlayerController!.stream.error.listen',
|
||||
const Duration(milliseconds: 10000), () {
|
||||
Future.delayed(const Duration(milliseconds: 3000), () async {
|
||||
if (kDebugMode) {
|
||||
debugPrint("isBuffering.value: ${isBuffering.value}");
|
||||
}
|
||||
if (kDebugMode) debugPrint("_buffered.value: ${_buffered.value}");
|
||||
if (isBuffering.value && _buffered.value == Duration.zero) {
|
||||
SmartDialog.showToast('视频链接打开失败,重试中',
|
||||
displayTime: const Duration(milliseconds: 500));
|
||||
if (!await refreshPlayer()) {
|
||||
if (kDebugMode) debugPrint("failed");
|
||||
EasyThrottle.throttle(
|
||||
'videoPlayerController!.stream.error.listen',
|
||||
const Duration(milliseconds: 10000),
|
||||
() {
|
||||
Future.delayed(const Duration(milliseconds: 3000), () async {
|
||||
if (kDebugMode) {
|
||||
debugPrint("isBuffering.value: ${isBuffering.value}");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (kDebugMode)
|
||||
debugPrint("_buffered.value: ${_buffered.value}");
|
||||
if (isBuffering.value && _buffered.value == Duration.zero) {
|
||||
SmartDialog.showToast(
|
||||
'视频链接打开失败,重试中',
|
||||
displayTime: const Duration(milliseconds: 500),
|
||||
);
|
||||
if (!await refreshPlayer()) {
|
||||
if (kDebugMode) debugPrint("failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
} else if (event.startsWith('Could not open codec')) {
|
||||
SmartDialog.showToast('无法加载解码器, $event,可能会切换至软解');
|
||||
} else {
|
||||
@@ -982,9 +1003,10 @@ class PlPlayerController {
|
||||
}),
|
||||
onPositionChanged.listen((Duration event) {
|
||||
EasyThrottle.throttle(
|
||||
'mediaServicePosition',
|
||||
const Duration(seconds: 1),
|
||||
() => videoPlayerServiceHandler.onPositionChange(event));
|
||||
'mediaServicePosition',
|
||||
const Duration(seconds: 1),
|
||||
() => videoPlayerServiceHandler.onPositionChange(event),
|
||||
);
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -1027,8 +1049,9 @@ class PlPlayerController {
|
||||
} else {
|
||||
if (kDebugMode) debugPrint('seek duration else');
|
||||
_timerForSeek?.cancel();
|
||||
_timerForSeek =
|
||||
Timer.periodic(const Duration(milliseconds: 200), (Timer t) async {
|
||||
_timerForSeek = Timer.periodic(const Duration(milliseconds: 200), (
|
||||
Timer t,
|
||||
) async {
|
||||
//_timerForSeek = null;
|
||||
if (_playerCount.value == 0) {
|
||||
_timerForSeek?.cancel();
|
||||
@@ -1174,8 +1197,10 @@ class PlPlayerController {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> setVolume(double volumeNew,
|
||||
{bool videoPlayerVolume = false}) async {
|
||||
Future<void> setVolume(
|
||||
double volumeNew, {
|
||||
bool videoPlayerVolume = false,
|
||||
}) async {
|
||||
if (volumeNew < 0.0) {
|
||||
volumeNew = 0.0;
|
||||
} else if (volumeNew > 1.0) {
|
||||
@@ -1235,8 +1260,10 @@ class PlPlayerController {
|
||||
|
||||
/// 缓存fit
|
||||
Future<void> setVideoFit() async {
|
||||
SmartDialog.showToast(_videoFit.value.toast,
|
||||
displayTime: const Duration(seconds: 1));
|
||||
SmartDialog.showToast(
|
||||
_videoFit.value.toast,
|
||||
displayTime: const Duration(seconds: 1),
|
||||
);
|
||||
video.put(VideoBoxKey.cacheVideoFit, _videoFit.value.index);
|
||||
}
|
||||
|
||||
@@ -1299,7 +1326,8 @@ class PlPlayerController {
|
||||
_longPressStatus.value = val;
|
||||
HapticFeedback.lightImpact();
|
||||
await setPlaybackSpeed(
|
||||
enableAutoLongPressSpeed ? playbackSpeed * 2 : longPressSpeed);
|
||||
enableAutoLongPressSpeed ? playbackSpeed * 2 : longPressSpeed,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// if (kDebugMode) debugPrint('$playbackSpeed');
|
||||
@@ -1385,8 +1413,9 @@ class PlPlayerController {
|
||||
|
||||
/// 截屏
|
||||
Future<Uint8List?> screenshot() async {
|
||||
final Uint8List? screenshot =
|
||||
await _videoPlayerController!.screenshot(format: 'image/png');
|
||||
final Uint8List? screenshot = await _videoPlayerController!.screenshot(
|
||||
format: 'image/png',
|
||||
);
|
||||
return screenshot;
|
||||
}
|
||||
|
||||
@@ -1419,7 +1448,8 @@ class PlPlayerController {
|
||||
if (videoType.value == 'live') {
|
||||
return;
|
||||
}
|
||||
bool isComplete = playerStatus.status.value == PlayerStatus.completed ||
|
||||
bool isComplete =
|
||||
playerStatus.status.value == PlayerStatus.completed ||
|
||||
type == 'completed';
|
||||
if ((durationSeconds.value - position.value).inMilliseconds > 1000) {
|
||||
isComplete = false;
|
||||
@@ -1539,14 +1569,17 @@ class PlPlayerController {
|
||||
|
||||
void setContinuePlayInBackground() {
|
||||
_continuePlayInBackground.value = !_continuePlayInBackground.value;
|
||||
setting.put(SettingBoxKey.continuePlayInBackground,
|
||||
_continuePlayInBackground.value);
|
||||
setting.put(
|
||||
SettingBoxKey.continuePlayInBackground,
|
||||
_continuePlayInBackground.value,
|
||||
);
|
||||
}
|
||||
|
||||
void setOnlyPlayAudio() {
|
||||
onlyPlayAudio.value = !onlyPlayAudio.value;
|
||||
videoPlayerController?.setVideoTrack(
|
||||
onlyPlayAudio.value ? VideoTrack.no() : VideoTrack.auto());
|
||||
onlyPlayAudio.value ? VideoTrack.no() : VideoTrack.auto(),
|
||||
);
|
||||
}
|
||||
|
||||
late final showSeekPreview = Pref.showSeekPreview;
|
||||
@@ -1593,12 +1626,12 @@ extension BoxFitExt on BoxFit {
|
||||
String get desc => const ['拉伸', '自动', '裁剪', '等宽', '等高', '原始', '限制'][index];
|
||||
|
||||
String get toast => const [
|
||||
'拉伸至播放器尺寸,将产生变形(竖屏改为自动)',
|
||||
'缩放至播放器尺寸,保留黑边',
|
||||
'缩放至填满播放器,裁剪超出部分',
|
||||
'缩放至撑满播放器宽度',
|
||||
'缩放至撑满播放器高度',
|
||||
'不缩放,以视频原始尺寸显示',
|
||||
'仅超出时缩小至播放器尺寸',
|
||||
][index];
|
||||
'拉伸至播放器尺寸,将产生变形(竖屏改为自动)',
|
||||
'缩放至播放器尺寸,保留黑边',
|
||||
'缩放至填满播放器,裁剪超出部分',
|
||||
'缩放至撑满播放器宽度',
|
||||
'缩放至撑满播放器高度',
|
||||
'不缩放,以视频原始尺寸显示',
|
||||
'仅超出时缩小至播放器尺寸',
|
||||
][index];
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ enum BtmProgressBehavior {
|
||||
alwaysShow('始终展示'),
|
||||
alwaysHide('始终隐藏'),
|
||||
onlyShowFullScreen('仅全屏时展示'),
|
||||
onlyHideFullScreen('仅全屏时隐藏'),
|
||||
;
|
||||
onlyHideFullScreen('仅全屏时隐藏');
|
||||
|
||||
final String desc;
|
||||
const BtmProgressBehavior(this.desc);
|
||||
|
||||
@@ -32,8 +32,9 @@ class DataSource {
|
||||
this.subFiles,
|
||||
required this.type,
|
||||
this.httpHeaders,
|
||||
}) : assert((type == DataSourceType.file && file != null) ||
|
||||
videoSource != null);
|
||||
}) : assert(
|
||||
(type == DataSourceType.file && file != null) || videoSource != null,
|
||||
);
|
||||
|
||||
DataSource copyWith({
|
||||
File? file,
|
||||
|
||||
@@ -11,8 +11,7 @@ enum FullScreenMode {
|
||||
// 屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏
|
||||
ratio('屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏'),
|
||||
// 强制重力转屏(仅安卓)
|
||||
gravity('忽略系统方向锁定,强制按重力转屏(仅安卓)'),
|
||||
;
|
||||
gravity('忽略系统方向锁定,强制按重力转屏(仅安卓)');
|
||||
|
||||
final String desc;
|
||||
const FullScreenMode(this.desc);
|
||||
|
||||
@@ -3,8 +3,7 @@ enum PlayRepeat {
|
||||
listOrder('顺序播放'),
|
||||
singleCycle('单个循环'),
|
||||
listCycle('列表循环'),
|
||||
autoPlayRelated('自动连播'),
|
||||
;
|
||||
autoPlayRelated('自动连播');
|
||||
|
||||
final String desc;
|
||||
const PlayRepeat(this.desc);
|
||||
|
||||
@@ -8,8 +8,7 @@ enum PlaySpeed {
|
||||
onePointSevenFive(1.75),
|
||||
|
||||
two(2.0),
|
||||
three(3.0),
|
||||
;
|
||||
three(3.0);
|
||||
|
||||
final double value;
|
||||
const PlaySpeed(this.value);
|
||||
|
||||
@@ -8,8 +8,9 @@ String printDuration(Duration? duration) {
|
||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||
|
||||
String twoDigitMinutes = twoDigits(duration.inMinutes).replaceAll("-", "");
|
||||
String twoDigitSeconds =
|
||||
twoDigits(duration.inSeconds.remainder(60)).replaceAll("-", "");
|
||||
String twoDigitSeconds = twoDigits(
|
||||
duration.inSeconds.remainder(60),
|
||||
).replaceAll("-", "");
|
||||
//customDebugPrint(duration.inSeconds.remainder(60));
|
||||
return "$twoDigitMinutes:$twoDigitSeconds";
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@ Future<void> landScape() async {
|
||||
} else if (Platform.isAndroid || Platform.isIOS) {
|
||||
await AutoOrientation.landscapeAutoMode(forceSensor: true);
|
||||
} else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
|
||||
await const MethodChannel('com.alexmercerind/media_kit_video')
|
||||
.invokeMethod(
|
||||
await const MethodChannel(
|
||||
'com.alexmercerind/media_kit_video',
|
||||
).invokeMethod(
|
||||
'Utils.EnterNativeFullscreen',
|
||||
);
|
||||
}
|
||||
@@ -84,8 +85,9 @@ Future<void> showStatusBar() async {
|
||||
overlays: SystemUiOverlay.values,
|
||||
);
|
||||
} else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
|
||||
await const MethodChannel('com.alexmercerind/media_kit_video')
|
||||
.invokeMethod(
|
||||
await const MethodChannel(
|
||||
'com.alexmercerind/media_kit_video',
|
||||
).invokeMethod(
|
||||
'Utils.ExitNativeFullscreen',
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,13 +18,16 @@ class AppBarAni extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
visible ? controller.forward() : controller.reverse();
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: Offset(0, position! == 'top' ? -1 : 1.1),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(
|
||||
parent: controller,
|
||||
curve: Curves.linear,
|
||||
)),
|
||||
position:
|
||||
Tween<Offset>(
|
||||
begin: Offset(0, position! == 'top' ? -1 : 1.1),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: controller,
|
||||
curve: Curves.linear,
|
||||
),
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: position! == 'top'
|
||||
|
||||
@@ -79,16 +79,20 @@ class BottomControl extends StatelessWidget {
|
||||
}
|
||||
if ((newProgress - lastAnnouncedValue).abs() > 0.02) {
|
||||
accessibilityDebounce?.cancel();
|
||||
accessibilityDebounce =
|
||||
Timer(const Duration(milliseconds: 200), () {
|
||||
SemanticsService.announce(
|
||||
accessibilityDebounce = Timer(
|
||||
const Duration(milliseconds: 200),
|
||||
() {
|
||||
SemanticsService.announce(
|
||||
"${(newProgress * 100).round()}%",
|
||||
TextDirection.ltr);
|
||||
lastAnnouncedValue = newProgress;
|
||||
});
|
||||
TextDirection.ltr,
|
||||
);
|
||||
lastAnnouncedValue = newProgress;
|
||||
},
|
||||
);
|
||||
}
|
||||
controller
|
||||
.onUpdatedSliderProgress(duration.timeStamp);
|
||||
controller.onUpdatedSliderProgress(
|
||||
duration.timeStamp,
|
||||
);
|
||||
},
|
||||
onSeek: (duration) {
|
||||
if (controller.showSeekPreview) {
|
||||
@@ -97,11 +101,14 @@ class BottomControl extends StatelessWidget {
|
||||
controller
|
||||
..onChangedSliderEnd()
|
||||
..onChangedSlider(duration.inSeconds.toDouble())
|
||||
..seekTo(Duration(seconds: duration.inSeconds),
|
||||
type: 'slider');
|
||||
..seekTo(
|
||||
Duration(seconds: duration.inSeconds),
|
||||
type: 'slider',
|
||||
);
|
||||
SemanticsService.announce(
|
||||
"${(duration.inSeconds / max * 100).round()}%",
|
||||
TextDirection.ltr);
|
||||
"${(duration.inSeconds / max * 100).round()}%",
|
||||
TextDirection.ltr,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (controller.segmentList.isNotEmpty)
|
||||
|
||||
Reference in New Issue
Block a user