mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-14 09:46:01 -07:00
feature: Added LibMDK video player backend (#162)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
6e32018183
commit
da354437e3
53 changed files with 1499 additions and 1006 deletions
|
|
@ -1,9 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/playback/playback_model.dart';
|
||||
import 'package:fladder/providers/session_info_provider.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
Future<void> showVideoPlaybackInformation(BuildContext context) {
|
||||
return showDialog(
|
||||
|
|
@ -19,6 +22,7 @@ class _VideoPlaybackInformation extends ConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final playbackModel = ref.watch(playBackModel);
|
||||
final sessionInfo = ref.watch(sessionInfoProvider);
|
||||
final backend = ref.read(videoPlayerProvider.select((value) => value.backend));
|
||||
return Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
|
|
@ -27,47 +31,81 @@ class _VideoPlaybackInformation extends ConsumerWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Playback information", style: Theme.of(context).textTheme.titleMedium),
|
||||
Text("Player info", style: Theme.of(context).textTheme.titleMedium),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4).copyWith(top: 4),
|
||||
child: Opacity(
|
||||
opacity: 0.80,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Text('backend: '), Text(backend?.label(context) ?? context.localized.unknown)],
|
||||
)
|
||||
].addPadding(const EdgeInsets.symmetric(vertical: 3)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
...[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Text('type: '), Text(playbackModel.label ?? "")],
|
||||
),
|
||||
if (sessionInfo.transCodeInfo != null) ...[
|
||||
const SizedBox(height: 6),
|
||||
Text("Transcoding", style: Theme.of(context).textTheme.titleMedium),
|
||||
if (sessionInfo.transCodeInfo?.transcodeReasons?.isNotEmpty == true)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Text('reason: '), Text(sessionInfo.transCodeInfo?.transcodeReasons.toString() ?? "")],
|
||||
),
|
||||
if (sessionInfo.transCodeInfo?.completionPercentage != null)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('transcode progress: '),
|
||||
Text("${sessionInfo.transCodeInfo?.completionPercentage?.toStringAsFixed(2)} %")
|
||||
Text("Playback information", style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 4),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4).copyWith(top: 4),
|
||||
child: Opacity(
|
||||
opacity: 0.8,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Text('type: '), Text(playbackModel.label ?? "")],
|
||||
),
|
||||
if (sessionInfo.transCodeInfo != null) ...[
|
||||
Text("Transcoding", style: Theme.of(context).textTheme.titleMedium),
|
||||
if (sessionInfo.transCodeInfo?.transcodeReasons?.isNotEmpty == true)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('reason: '),
|
||||
Text(sessionInfo.transCodeInfo?.transcodeReasons.toString() ?? "")
|
||||
],
|
||||
),
|
||||
if (sessionInfo.transCodeInfo?.completionPercentage != null)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('transcode progress: '),
|
||||
Text("${sessionInfo.transCodeInfo?.completionPercentage?.toStringAsFixed(2)} %")
|
||||
],
|
||||
),
|
||||
if (sessionInfo.transCodeInfo?.container != null)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('container: '),
|
||||
Text(sessionInfo.transCodeInfo!.container.toString())
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (sessionInfo.transCodeInfo?.container != null)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Text('container: '), Text(sessionInfo.transCodeInfo!.container.toString())],
|
||||
),
|
||||
],
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Text('resolution: '), Text(playbackModel?.item.streamModel?.resolutionText ?? "")],
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('resolution: '),
|
||||
Text(playbackModel?.item.streamModel?.resolutionText ?? "")
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('container: '),
|
||||
Text(playbackModel?.playbackInfo?.mediaSources?.firstOrNull?.container ?? "")
|
||||
],
|
||||
)
|
||||
].addPadding(const EdgeInsets.symmetric(vertical: 3)),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('container: '),
|
||||
Text(playbackModel?.playbackInfo?.mediaSources?.firstOrNull?.container ?? "")
|
||||
],
|
||||
),
|
||||
].addPadding(const EdgeInsets.symmetric(vertical: 3))
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/screens/video_player/components/video_player_chapters.dart';
|
||||
|
|
@ -9,8 +8,7 @@ import 'package:fladder/screens/video_player/components/video_player_queue.dart'
|
|||
|
||||
class ChapterButton extends ConsumerWidget {
|
||||
final Duration position;
|
||||
final Player player;
|
||||
const ChapterButton({super.key, required this.position, required this.player});
|
||||
const ChapterButton({super.key, required this.position});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -22,9 +20,9 @@ class ChapterButton extends ConsumerWidget {
|
|||
context,
|
||||
chapters: currentChapters,
|
||||
currentPosition: position,
|
||||
onChapterTapped: (chapter) => player.seek(
|
||||
chapter.startPosition,
|
||||
),
|
||||
onChapterTapped: (chapter) => ref.read(videoPlayerProvider).seek(
|
||||
chapter.startPosition,
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
|
|
|
|||
|
|
@ -447,13 +447,13 @@ class _SimpleControls extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final player = ref.watch(videoPlayerProvider.select((value) => value.controller?.player));
|
||||
final player = ref.watch(videoPlayerProvider);
|
||||
final isPlaying = ref.watch(mediaPlaybackProvider.select((value) => value.playing));
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton.filledTonal(
|
||||
onPressed: () => player?.playOrPause(),
|
||||
onPressed: () => player.playOrPause(),
|
||||
icon: Icon(isPlaying ? IconsaxBold.pause : IconsaxBold.play),
|
||||
),
|
||||
if (skip != null)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import 'package:fladder/models/playback/direct_playback_model.dart';
|
|||
import 'package:fladder/models/playback/offline_playback_model.dart';
|
||||
import 'package:fladder/models/playback/playback_model.dart';
|
||||
import 'package:fladder/models/playback/transcode_playback_model.dart';
|
||||
import 'package:fladder/models/settings/video_player_settings.dart';
|
||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
|
|
@ -375,15 +376,16 @@ Future<void> showSubSelection(BuildContext context) {
|
|||
children: [
|
||||
Text(context.localized.subtitle),
|
||||
const Spacer(),
|
||||
IconButton.outlined(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showSubtitleControls(
|
||||
context: context,
|
||||
label: context.localized.subtitleConfiguration,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.display_settings_rounded))
|
||||
if (player.backend == PlayerOptions.libMPV)
|
||||
IconButton.outlined(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showSubtitleControls(
|
||||
context: context,
|
||||
label: context.localized.subtitleConfiguration,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.display_settings_rounded))
|
||||
],
|
||||
),
|
||||
children: playbackModel?.subStreams?.mapIndexed(
|
||||
|
|
@ -459,7 +461,7 @@ Future<void> showPlaybackSpeed(BuildContext context) {
|
|||
return StatefulBuilder(builder: (context, setState) {
|
||||
return Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final player = ref.watch(videoPlayerProvider.select((value) => value.player));
|
||||
final player = ref.watch(videoPlayerProvider);
|
||||
final lastSpeed = ref.watch(playbackRateProvider);
|
||||
return SimpleDialog(
|
||||
contentPadding: const EdgeInsets.only(top: 8, bottom: 24),
|
||||
|
|
@ -484,7 +486,7 @@ Future<void> showPlaybackSpeed(BuildContext context) {
|
|||
divisions: 39,
|
||||
onChanged: (value) {
|
||||
ref.read(playbackRateProvider.notifier).state = value;
|
||||
player?.setRate(value);
|
||||
player.setSpeed(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class _ChapterProgressSliderState extends ConsumerState<VideoProgressBar> {
|
|||
setState(() {
|
||||
onHoverStart = true;
|
||||
});
|
||||
widget.wasPlayingChanged.call(player.player?.state.playing ?? false);
|
||||
widget.wasPlayingChanged.call(player.lastState?.playing ?? false);
|
||||
player.pause();
|
||||
},
|
||||
onChanged: (e) {
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:fladder/providers/settings/subtitle_settings_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
|
||||
import 'package:fladder/models/settings/subtitle_settings_model.dart';
|
||||
|
||||
class VideoSubtitles extends ConsumerStatefulWidget {
|
||||
final VideoController controller;
|
||||
final bool overLayed;
|
||||
const VideoSubtitles({
|
||||
required this.controller,
|
||||
this.overLayed = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() => _VideoSubtitlesState();
|
||||
}
|
||||
|
||||
class _VideoSubtitlesState extends ConsumerState<VideoSubtitles> {
|
||||
late List<String> subtitle = widget.controller.player.state.subtitle;
|
||||
StreamSubscription<List<String>>? subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
subscription = widget.controller.player.stream.subtitle.listen((value) {
|
||||
setState(() {
|
||||
subtitle = value;
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
subscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settings = ref.watch(subtitleSettingsProvider);
|
||||
final padding = MediaQuery.of(context).padding;
|
||||
final text = [
|
||||
for (final line in subtitle)
|
||||
if (line.trim().isNotEmpty) line.trim(),
|
||||
].join('\n');
|
||||
|
||||
if (widget.controller.player.platform?.configuration.libass ?? false) {
|
||||
return const IgnorePointer(child: SizedBox());
|
||||
} else {
|
||||
return SubtitleText(
|
||||
subModel: settings,
|
||||
padding: padding,
|
||||
offset: (widget.overLayed ? 0.5 : settings.verticalOffset),
|
||||
text: text,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue