@ -1,64 +1,107 @@
import ' package:flutter/gestures.dart ' show TapGestureRecognizer ;
import ' package:flutter/widgets .dart ' ;
import ' package:flutter/material .dart ' ;
import ' package:flutter_quill/flutter_quill.dart ' show DefaultStyles ;
import ' package:url_launcher/url_launcher.dart ' show launchUrl ;
import ' package:url_launcher/url_launcher_string.dart ' ;
import ' package:youtube_explode_dart/youtube_explode_dart.dart ' ;
import ' package:youtube_player_flutter/youtube_player_flutter.dart ' ;
import ' ../../models/config/video/editor/youtube_video_support_mode.dart ' ;
import ' video_app.dart ' ;
class YoutubeVideoApp extends StatefulWidget {
const YoutubeVideoApp ( {
required this . videoUrl ,
required this . readOnly ,
required this . youtubeVideoSupportMode ,
super . key ,
} ) ;
final String videoUrl ;
final bool readOnly ;
final YoutubeVideoSupportMode youtubeVideoSupportMode ;
@ override
YoutubeVideoAppState createState ( ) = > YoutubeVideoAppState ( ) ;
}
class YoutubeVideoAppState extends State < YoutubeVideoApp > {
YoutubePlayerController ? _youtubeController ;
YoutubePlayerController ? _youtubeIframeController ;
/ / / On some platforms such as desktop , Webview is not supported yet
/ / / as a result the youtube video player package is not supported too
/ / / this future will be not null and fetch the video url to load it using
/ / / [ VideoApp ]
Future < String > ? _loadYoutubeVideoByDownloadUrlFuture ;
/ / / Null if the video URL is not a YouTube video
String ? get _videoId {
return YoutubePlayer . convertUrlToId ( widget . videoUrl ) ;
}
@ override
void initState ( ) {
super . initState ( ) ;
final videoId = YoutubePlayer . convertUrlToId ( widget . videoUrl ) ;
if ( videoId ! = null ) {
_youtubeController = YoutubePlayerController (
final videoId = _videoId ;
if ( videoId = = null ) {
return ;
}
switch ( widget . youtubeVideoSupportMode ) {
case YoutubeVideoSupportMode . disabled:
break ;
case YoutubeVideoSupportMode . iframeView:
_youtubeIframeController = YoutubePlayerController (
initialVideoId: videoId ,
flags: const YoutubePlayerFlags (
autoPlay: false ,
) ,
) ;
break ;
case YoutubeVideoSupportMode . customPlayerWithDownloadUrl:
_loadYoutubeVideoByDownloadUrlFuture =
_loadYoutubeVideoWithVideoPlayerByVideoUrl ( ) ;
break ;
}
}
@ override
Widget build ( BuildContext context ) {
final defaultStyles = DefaultStyles . getInstance ( context ) ;
final youtubeController = _youtubeController ;
Future < String > _loadYoutubeVideoWithVideoPlayerByVideoUrl ( ) async {
final youtubeExplode = YoutubeExplode ( ) ;
final manifest =
await youtubeExplode . videos . streamsClient . getManifest ( _videoId ) ;
final streamInfo = manifest . muxed . withHighestBitrate ( ) ;
final videoDownloadUri = streamInfo . url ;
return videoDownloadUri . toString ( ) ;
}
if ( youtubeController = = null ) {
if ( widget . readOnly ) {
Widget _clickableVideoLinkText ( { required DefaultStyles defaultStyles } ) {
return RichText (
text: TextSpan (
text: widget . videoUrl ,
style: defaultStyles . link ,
recognizer: TapGestureRecognizer ( )
. . onTap = ( ) = > launchUrl (
Uri . parse ( widget . videoUrl ) ,
) ,
. . onTap = ( ) = > launchUrlString ( widget . videoUrl ) ,
) ,
) ;
}
@ override
Widget build ( BuildContext context ) {
final defaultStyles = DefaultStyles . getInstance ( context ) ;
switch ( widget . youtubeVideoSupportMode ) {
case YoutubeVideoSupportMode . disabled:
throw UnsupportedError ( ' YouTube video links are not supported ' ) ;
case YoutubeVideoSupportMode . iframeView:
final youtubeController = _youtubeIframeController ;
if ( youtubeController = = null ) {
if ( widget . readOnly ) {
return _clickableVideoLinkText ( defaultStyles: defaultStyles ) ;
}
return RichText (
text: TextSpan ( text: widget . videoUrl , style: defaultStyles . link ) ,
) ;
}
return YoutubePlayerBuilder (
player: YoutubePlayer (
controller: youtubeController ,
@ -68,11 +111,33 @@ class YoutubeVideoAppState extends State<YoutubeVideoApp> {
return player ;
} ,
) ;
case YoutubeVideoSupportMode . customPlayerWithDownloadUrl:
assert (
_loadYoutubeVideoByDownloadUrlFuture ! = null ,
' The load youtube video future should not null for " ${ widget . youtubeVideoSupportMode } " mode ' ,
) ;
return FutureBuilder < String > (
future: _loadYoutubeVideoByDownloadUrlFuture ,
builder: ( context , snapshot ) {
if ( snapshot . connectionState = = ConnectionState . waiting ) {
return const Center ( child: CircularProgressIndicator . adaptive ( ) ) ;
}
if ( snapshot . hasError ) {
return _clickableVideoLinkText ( defaultStyles: defaultStyles ) ;
}
return VideoApp (
videoUrl: snapshot . requireData ,
readOnly: widget . readOnly ,
) ;
} ,
) ;
}
}
@ override
void dispose ( ) {
_youtubeController ? . dispose ( ) ;
_youtubeIframe Controller ? . dispose ( ) ;
super . dispose ( ) ;
}
}