11import 'dart:async' ;
2+
23import 'package:flutter/material.dart' ;
34import 'package:flutter/services.dart' ;
5+ import 'package:native_device_orientation/native_device_orientation.dart' ;
46import 'package:provider/provider.dart' ;
57import 'package:wakelock_plus/wakelock_plus.dart' ;
68
7- import 'models/player.dart' ;
9+ import 'chat.dart' ;
10+ import 'chat_panel.dart' ;
811import 'models/app.dart' ;
12+ import 'models/player.dart' ;
913import 'playlist.dart' ;
10- import 'chat_panel.dart' ;
1114import 'settings.dart' ;
1215import 'video_player.dart' ;
1316import 'wsdata.dart' ;
14- import 'chat.dart' ;
15- import 'package:native_device_orientation/native_device_orientation.dart' ;
1617// import 'color_scheme.dart';
1718
1819typedef WsDataFunc = void Function (WsData data);
@@ -40,19 +41,22 @@ class _AppState extends State<App> with WidgetsBindingObserver {
4041 WakelockPlus .enable ();
4142 WidgetsBinding .instance.addObserver (this );
4243 final nativeOrientation = NativeDeviceOrientationCommunicator ();
43- final orientationStream =
44- nativeOrientation.onOrientationChanged (useSensor: true );
44+ final orientationStream = nativeOrientation.onOrientationChanged (
45+ useSensor: true ,
46+ );
4547 orientationListener = orientationStream.listen ((event) {
4648 switch (event) {
4749 case NativeDeviceOrientation .landscapeLeft:
4850 if (Settings .prefferedOrientations.isEmpty) return ;
49- SystemChrome .setPreferredOrientations (
50- [DeviceOrientation .landscapeLeft]);
51+ SystemChrome .setPreferredOrientations ([
52+ DeviceOrientation .landscapeLeft,
53+ ]);
5154 break ;
5255 case NativeDeviceOrientation .landscapeRight:
5356 if (Settings .prefferedOrientations.isEmpty) return ;
54- SystemChrome .setPreferredOrientations (
55- [DeviceOrientation .landscapeRight]);
57+ SystemChrome .setPreferredOrientations ([
58+ DeviceOrientation .landscapeRight,
59+ ]);
5660 break ;
5761 default :
5862 }
@@ -117,41 +121,42 @@ class _AppState extends State<App> with WidgetsBindingObserver {
117121 : Axis .vertical,
118122 children: [
119123 Selector <AppModel , bool >(
120- selector: (context, app) => app.isChatVisible,
121- builder: (context, isChatVisible, child) {
122- return Selector <PlayerModel , double >(
123- selector: (context, player) {
124- final isInit =
125- player.controller? .value.isInitialized ?? false ;
126- if (! isInit) return 16 / 9 ;
127- return player.controller? .value.aspectRatio ?? 16 / 9 ;
128- },
129- builder: (context, ratio, child) {
130- final media = MediaQuery .of (context);
131- return Container (
132- color: Colors .black,
133- padding: EdgeInsets .only (
134- top: _isKeyboardVisible () ? media.padding.top : 0 ,
135- ),
136- width: orientation == Orientation .landscape
137- ? isChatVisible
124+ selector: (context, app) => app.isChatVisible,
125+ builder: (context, isChatVisible, child) {
126+ return Selector <PlayerModel , double >(
127+ selector: (context, player) {
128+ final isInit =
129+ player.controller? .value.isInitialized ?? false ;
130+ if (! isInit) return 16 / 9 ;
131+ return player.controller? .value.aspectRatio ?? 16 / 9 ;
132+ },
133+ builder: (context, ratio, child) {
134+ final media = MediaQuery .of (context);
135+ return Container (
136+ color: Colors .black,
137+ padding: EdgeInsets .only (
138+ top: _isKeyboardVisible () ? media.padding.top : 0 ,
139+ ),
140+ width: orientation == Orientation .landscape
141+ ? isChatVisible
138142 ? media.size.width / 1.5
139143 : media.size.width
140- : double .infinity,
141- height: playerHeight (ratio),
142- child: Column (
143- children: [
144- if (orientation == Orientation .landscape &&
145- ! _isKeyboardVisible () &&
146- isChatVisible)
147- ChatPanel (),
148- Expanded (child: VideoPlayerScreen ()),
149- ],
150- ),
151- );
152- },
153- );
154- }),
144+ : double .infinity,
145+ height: playerHeight (ratio),
146+ child: Column (
147+ children: [
148+ if (orientation == Orientation .landscape &&
149+ ! _isKeyboardVisible () &&
150+ isChatVisible)
151+ ChatPanel (),
152+ Expanded (child: VideoPlayerScreen ()),
153+ ],
154+ ),
155+ );
156+ },
157+ );
158+ },
159+ ),
155160 Selector <AppModel , bool >(
156161 selector: (context, app) => app.isChatVisible,
157162 builder: (context, isChatVisible, child) {
@@ -282,8 +287,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
282287 }
283288
284289 String getPlayerType (String url) {
285- final playerType =
286- PlayerModel .extractVideoId (url).isEmpty ? 'RawType' : 'YoutubeType' ;
290+ final playerType = PlayerModel .extractVideoId (url).isEmpty
291+ ? 'RawType'
292+ : 'YoutubeType' ;
287293 return playerType;
288294 }
289295
@@ -307,6 +313,11 @@ class _AppState extends State<App> with WidgetsBindingObserver {
307313 clipboardText.contains ('youtu' )) {
308314 url = clipboardText;
309315 }
316+ if (clipboardText.endsWith ('.ts' )) {
317+ url = clipboardText.replaceAll ('240.mp4' , '1080.mp4' );
318+ // `1080.mp4:hls:seg-123-v1-a1.ts` => `1080.mp4:hls:manifest.m3u8`
319+ url = url.replaceAll (RegExp (r'seg-[^:]+\.ts$' ), 'manifest.m3u8' );
320+ }
310321 final data = AddVideo (
311322 item: VideoList (
312323 url: url,
@@ -329,10 +340,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
329340 return StatefulBuilder (
330341 builder: (context, setState) {
331342 return AlertDialog (
332- backgroundColor: Theme .of (context)
333- .dialogTheme
334- .backgroundColor!
335- .withAlpha (bgAlpha),
343+ backgroundColor: Theme .of (
344+ context,
345+ ).dialogTheme.backgroundColor! .withAlpha (bgAlpha),
336346 insetPadding: EdgeInsets .zero,
337347 scrollable: true ,
338348 content: SingleChildScrollView (
@@ -341,23 +351,25 @@ class _AppState extends State<App> with WidgetsBindingObserver {
341351 mainAxisSize: MainAxisSize .min,
342352 children: [
343353 TextFormField (
344- initialValue: data.item.url,
345- autofocus: url == defaultUrl,
346- decoration: const InputDecoration (
347- labelText: 'Video URL' ,
348- ),
349- onChanged: (value) {
350- onUrlUpdate (data, value);
351- setState (() => {});
352- }),
354+ initialValue: data.item.url,
355+ autofocus: url == defaultUrl,
356+ decoration: const InputDecoration (
357+ labelText: 'Video URL' ,
358+ ),
359+ onChanged: (value) {
360+ onUrlUpdate (data, value);
361+ setState (() => {});
362+ },
363+ ),
353364 TextFormField (
354365 decoration: const InputDecoration (
355366 labelText: 'Subtitles URL' ,
356367 ),
357368 onChanged: (value) => data.item.subs = value,
358369 ),
359- if (app.playersCacheSupport
360- .contains (getPlayerType (data.item.url)))
370+ if (app.playersCacheSupport.contains (
371+ getPlayerType (data.item.url),
372+ ))
361373 CheckboxListTile (
362374 title: const Text ('Cache' ),
363375 value: data.item.doCache,
@@ -407,10 +419,13 @@ class _AppState extends State<App> with WidgetsBindingObserver {
407419 app.dispose ();
408420 orientationListener? .cancel ();
409421 SystemChrome .setPreferredOrientations ([]);
410- SystemChrome .setEnabledSystemUIMode (SystemUiMode .manual, overlays: [
411- SystemUiOverlay .top,
412- SystemUiOverlay .bottom,
413- ]);
422+ SystemChrome .setEnabledSystemUIMode (
423+ SystemUiMode .manual,
424+ overlays: [
425+ SystemUiOverlay .top,
426+ SystemUiOverlay .bottom,
427+ ],
428+ );
414429 WakelockPlus .disable ();
415430 WidgetsBinding .instance.removeObserver (this );
416431 }
0 commit comments