Skip to content

Commit

Permalink
fix: media_kit player
Browse files Browse the repository at this point in the history
  • Loading branch information
prateekmedia committed Jul 21, 2023
1 parent 26bc0d2 commit 18b753e
Show file tree
Hide file tree
Showing 13 changed files with 319 additions and 106 deletions.
2 changes: 1 addition & 1 deletion lib/foundation/extensions/int/extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extension IntExtension on int {
String get formatNumber => NumberFormat.compact().format(this);

String getFileSize({int decimals = 1}) {
if (this <= 0) return '0.0 KB';
if (this <= 0) return '';
final suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
final i = (log(this) / log(1024)).floor();
return '${(this / pow(1024, i)).toStringAsFixed(decimals)} ${suffixes[i]}';
Expand Down
8 changes: 8 additions & 0 deletions lib/foundation/extensions/string/extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ extension UrlLauncher on String {
micros = (double.parse(parts[parts.length - 1]) * 1000000).round();
return Duration(hours: hours, minutes: minutes, microseconds: micros);
}

int get encodeToInt {
var sum = 0;
for (var i = 0; i < length; i++) {
sum += codeUnitAt(i);
}
return sum;
}
}

extension NullableExtension on String? {
Expand Down
21 changes: 15 additions & 6 deletions lib/foundation/show_download_popover.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:libadwaita/libadwaita.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:pstube/data/models/models.dart';
Expand Down Expand Up @@ -223,15 +223,24 @@ class DownloadQualityTile extends HookConsumerWidget {
final size = useState<int>(0);

Future<void> getSize() async {
final dio = Dio();
final client = http.Client();
try {
// ignore: inference_failure_on_function_invocation
final r = await dio.head(stream.url);
if (context.mounted) {
size.value = int.tryParse(r.headers['content-length']![0]) ?? 0;
final r = await client.head(
Uri.parse(stream.url),
);
if (r.statusCode == 200) {
final length = r.headers['content-length'];
if (length != null) {
final sizeP = int.tryParse(length);
if (sizeP != null && context.mounted) {
size.value = sizeP;
}
}
}
} catch (e) {
debugPrint('$e');
} finally {
client.close();
}
}

Expand Down
9 changes: 9 additions & 0 deletions lib/ui/screens/home_page/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,15 @@ class MyHomePage extends HookConsumerWidget {
],
),
),
// Offstage(
// offstage: ref.watch(selectedVideoProvider) != null,
// child: Miniplayer(
// minHeight: 60,
// controller: ref.read(miniPlayerControllerProvider),
// maxHeight: double.infinity,
// builder: (height, percentage) => VideoScreen(),
// ),
// )
],
),
viewSwitcher: !toggleSearch.value
Expand Down
6 changes: 6 additions & 0 deletions lib/ui/screens/home_page/state/mini_player_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:miniplayer/miniplayer.dart';

final miniPlayerControllerProvider = Provider<MiniplayerController>((ref) {
return MiniplayerController();
});
5 changes: 5 additions & 0 deletions lib/ui/screens/video_screen/state/selected_video.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';

final selectedVideoProvider = StateProvider<String?>((ref) {
return null;
});
7 changes: 3 additions & 4 deletions lib/ui/widgets/channel_logo.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:math';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand Down Expand Up @@ -29,8 +27,9 @@ class _ChannelLogoState extends State<ChannelLogo>
super.build(context);
final channelHasData = widget.channel != null;
final channelData = channelHasData ? widget.channel! : null;
final Color bgColor =
Colors.primaries[Random().nextInt(Colors.primaries.length)];
final Color bgColor = Colors.primaries[
(channelData?.name ?? widget.author ?? '').encodeToInt %
Colors.primaries.length];

final Widget defaultPlaceholder = Container(
width: widget.size,
Expand Down
171 changes: 130 additions & 41 deletions lib/ui/widgets/video_player_desktop/vid_player_mpv.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:io';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video_controls/widgets/widgets.dart';

class VideoPlayerMpv extends StatefulWidget {
const VideoPlayerMpv({
Expand All @@ -24,65 +26,152 @@ class VideoPlayerMpv extends StatefulWidget {
}

class _VideoPlayerMpvState extends State<VideoPlayerMpv> {
final Player player = Player(
configuration: const PlayerConfiguration(
logLevel: MPVLogLevel.warn,
),
);
MediaKitController? mediaKitController;
late VideoController videoController;
// Create a [Player] to control playback.
late final player = Player();
// Create a [VideoController] to handle video output from [Player].
late final controller = VideoController(player);
late List<Media> medias = <Media>[Media(widget.url)];
late Map<int, String> aud = widget.audstreams;
late Map<String, String> res = widget.resolutions;
late Map<int, int> aspect = widget.handw;
late double aspectvalue;
late String quality = 'Auto';

@override
void initState() {
super.initState();
mediaKitController = MediaKitController(
player: player,
autoPlay: true,
looping: true,
);
_selectResolution(widget.resolutions.values.first);
}

Future<void> _selectResolution(String vid) async {
// The maximum bitrate is needed to select the best audio quality
final maxBitrate = aud.keys.reduce(max);
// The audio URL is needed to load the audio track
final audioUrl = aud[maxBitrate]!;

if (player.platform != null && player.platform is libmpvPlayer) {
try {
// The audio track is appended to the video track using the libmpvPlayer method
await (player.platform! as libmpvPlayer).setProperty(
'audio-files',
Platform.isWindows
? audioUrl.replaceAll(';', r'\;')
: audioUrl.replaceAll(':', r'\:'),
);
} catch (e) {
debugPrint('External Audio error: $e');
}
}

videoController = VideoController(player);
setState(() {});
// The playlist with both audio and video tracks is opened by the player
// await player.open(Playlist(medias));
// Alternatively, only the video track can be opened by the player
await player.open(Playlist([Media(vid)]));

setState(() {
final aspectlist = aspect.entries.toList();
final h = aspectlist[0].key;
final w = aspectlist[0].value;

aspectvalue = h / w;
});
}

@override
void dispose() {
player.dispose();
mediaKitController?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 3,
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Card(
elevation: 8,
clipBehavior: Clip.antiAlias,
margin: const EdgeInsets.all(32),
child: MediaKitControls(
controller: mediaKitController!,
video: Video(controller: videoController),
),
),
),
const SizedBox(height: 32),
],
child: SizedBox(
height: 500,
// Use [Video] widget to display video output.
child: MaterialDesktopVideoControlsTheme(
fullscreen: MaterialDesktopVideoControlsThemeData(
bottomButtonBar: [
const MaterialDesktopSkipPreviousButton(),
const MaterialDesktopPlayOrPauseButton(),
const MaterialDesktopSkipNextButton(),
const MaterialDesktopVolumeButton(),
const MaterialDesktopPositionIndicator(),
const Spacer(),
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.black,
),
onPressed: () {
showDialog<dynamic>(
context: context,
builder: (context) {
return resolutionDialog();
},
);
},
child: Text(quality),
),
),
const MaterialDesktopFullscreenButton(),
],
),
],
normal: MaterialDesktopVideoControlsThemeData(
bottomButtonBar: [
const MaterialDesktopSkipPreviousButton(),
const MaterialDesktopPlayOrPauseButton(),
const MaterialDesktopSkipNextButton(),
const MaterialDesktopVolumeButton(),
const MaterialDesktopPositionIndicator(),
const Spacer(),
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.black,
),
onPressed: () {
showDialog<dynamic>(
context: context,
builder: (context) {
return resolutionDialog();
},
);
},
child: Text(quality),
),
const MaterialDesktopFullscreenButton(),
],
),
child: Video(
controller: controller,
),
),
),
);
}

SimpleDialog resolutionDialog() {
return SimpleDialog(
title: const Text('Resolutions'),
children: res.entries.map((entry) {
final w = InkWell(
onTap: () {
setState(() {
quality = entry.key;
});
_selectResolution(entry.value);
Navigator.pop(context);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
entry.key,
),
),
);

return w;
}).toList(),
);
}
}
4 changes: 4 additions & 0 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,28 @@ import connectivity_plus
import media_kit_video
import package_info_plus
import path_provider_foundation
import screen_brightness_macos
import screen_retriever
import share_plus
import shared_preferences_foundation
import sqflite
import url_launcher_macos
import wakelock_macos
import wakelock_plus
import window_manager

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}
Loading

0 comments on commit 18b753e

Please sign in to comment.