Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-07-23 16:47:11 +08:00
parent 148e0872b4
commit 418a1e8d39
821 changed files with 29467 additions and 25520 deletions

View File

@@ -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];
}

View File

@@ -2,8 +2,7 @@ enum BtmProgressBehavior {
alwaysShow('始终展示'),
alwaysHide('始终隐藏'),
onlyShowFullScreen('仅全屏时展示'),
onlyHideFullScreen('仅全屏时隐藏'),
;
onlyHideFullScreen('仅全屏时隐藏');
final String desc;
const BtmProgressBehavior(this.desc);

View File

@@ -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,

View File

@@ -11,8 +11,7 @@ enum FullScreenMode {
// 屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏
ratio('屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏'),
// 强制重力转屏(仅安卓)
gravity('忽略系统方向锁定,强制按重力转屏(仅安卓)'),
;
gravity('忽略系统方向锁定,强制按重力转屏(仅安卓)');
final String desc;
const FullScreenMode(this.desc);

View File

@@ -3,8 +3,7 @@ enum PlayRepeat {
listOrder('顺序播放'),
singleCycle('单个循环'),
listCycle('列表循环'),
autoPlayRelated('自动连播'),
;
autoPlayRelated('自动连播');
final String desc;
const PlayRepeat(this.desc);

View File

@@ -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);

View File

@@ -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";
}

View File

@@ -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

View File

@@ -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'

View File

@@ -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)