mirror of
https://github.com/A-Star100/simpliplay-android.git
synced 2025-09-18 06:29:39 +00:00
Modified source for improvement of management
This commit is contained in:
parent
3cef0cf007
commit
9bb3bf06c6
@ -1,6 +1,6 @@
|
|||||||
# flutter_exoplayer_creator
|
# media3_exoplayer_creator
|
||||||
|
|
||||||
Flutter version of ExoPlayer Creator
|
ExoPlayer Creator that supports Media3
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<!-- Add Internet and storage permissions -->
|
<!-- Add Internet and storage permissions -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> -->
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> -->
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:media3_exoplayer_creator/screens/video_screen.dart'; // Import other files as needed
|
||||||
import 'package:chewie/chewie.dart';
|
|
||||||
import 'package:keep_screen_on/keep_screen_on.dart'; // Import the keep_screen_on package
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(MyApp());
|
runApp(MyApp());
|
||||||
@ -15,162 +13,43 @@ class MyApp extends StatelessWidget {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
primarySwatch: Colors.blue, // Set primary color to blue
|
primarySwatch: Colors.blue, // Set primary color to blue
|
||||||
colorScheme: ColorScheme.light(
|
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue).copyWith(
|
||||||
primary: Colors.blue, // Primary color
|
secondary: Colors.blue, // Set accent (secondary) color to blue accent
|
||||||
secondary: Colors.blueAccent, // Accent color (replaces accentColor)
|
|
||||||
),
|
),
|
||||||
appBarTheme: AppBarTheme(
|
appBarTheme: AppBarTheme(
|
||||||
backgroundColor: Colors.blue, // Set AppBar background to blue
|
color: Colors.blue, // Set the AppBar color to blue
|
||||||
),
|
titleTextStyle: TextStyle(
|
||||||
buttonTheme: ButtonThemeData(
|
color: Colors.white, // Set text color to white for AppBar
|
||||||
buttonColor: Colors.blue, // Set button color to blue
|
fontSize: 20,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
// Customize other theme properties as needed
|
|
||||||
),
|
|
||||||
home: VideoURLScreen(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VideoURLScreen extends StatefulWidget {
|
|
||||||
const VideoURLScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_VideoURLScreenState createState() => _VideoURLScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _VideoURLScreenState extends State<VideoURLScreen> {
|
|
||||||
String _videoUrl = '';
|
|
||||||
|
|
||||||
// Show the dialog to ask for the video URL
|
|
||||||
Future<void> _showVideoURLDialog() async {
|
|
||||||
final TextEditingController videoController = TextEditingController();
|
|
||||||
|
|
||||||
return showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false, // Prevent dismissing by tapping outside
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text('Enter Video URL'),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
controller: videoController,
|
|
||||||
decoration: InputDecoration(hintText: 'Enter a valid video URL'),
|
|
||||||
keyboardType: TextInputType.url,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: Text('Cancel'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: Text('OK'),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_videoUrl = videoController.text;
|
|
||||||
});
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text('ExoPlayer Creator (New Edition)'),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: _videoUrl.isEmpty
|
|
||||||
? ElevatedButton(
|
|
||||||
onPressed: _showVideoURLDialog,
|
|
||||||
child: Text('Enter Video URL'),
|
|
||||||
)
|
|
||||||
: VideoPlayerWidget(videoUrl: _videoUrl),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VideoPlayerWidget extends StatefulWidget {
|
|
||||||
final String videoUrl;
|
|
||||||
|
|
||||||
const VideoPlayerWidget({super.key, required this.videoUrl});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_VideoPlayerWidgetState createState() => _VideoPlayerWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _VideoPlayerWidgetState extends State<VideoPlayerWidget> {
|
|
||||||
late VideoPlayerController _videoPlayerController;
|
|
||||||
late ChewieController _chewieController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)); // Convert String to Uri
|
|
||||||
|
|
||||||
_chewieController = ChewieController(
|
|
||||||
videoPlayerController: _videoPlayerController,
|
|
||||||
aspectRatio: 16 / 9,
|
|
||||||
autoPlay: true,
|
|
||||||
looping: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Enable keep_screen_on to prevent screen sleep
|
|
||||||
_videoPlayerController.addListener(() {
|
|
||||||
if (_videoPlayerController.value.isPlaying) {
|
|
||||||
KeepScreenOn.turnOn(); // Keep the screen on while video is playing
|
|
||||||
} else {
|
|
||||||
KeepScreenOn.turnOff(); // Allow the screen to turn off when the video is paused or stopped
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
_chewieController.dispose();
|
|
||||||
_videoPlayerController.dispose();
|
|
||||||
KeepScreenOn.turnOff(); // Ensure screen turns off when the widget is disposed
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Chewie(
|
|
||||||
controller: _chewieController,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm exit when back is pressed
|
|
||||||
Future<bool> _onWillPop(BuildContext context) async {
|
|
||||||
return await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: Text('Are you sure?'),
|
|
||||||
content: Text('If you exit now, ExoPlayer will stop playing.'),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
|
||||||
child: Text('No'),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
textButtonTheme: TextButtonThemeData(
|
||||||
onPressed: () => Navigator.of(context).pop(true),
|
style: TextButton.styleFrom(
|
||||||
child: Text('Yes'),
|
foregroundColor: Colors.blueAccent, // Set text color to blue for TextButtons
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
),
|
filled: true, // Allow filled background for text fields
|
||||||
) ?? false;
|
fillColor: Colors.white, // White background for text fields
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8), // Rounded corners for text fields
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.blueAccent, // Border color
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.blueAccent, // Focused border color
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
home: VideoScreen(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,176 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:keep_screen_on/keep_screen_on.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart'; // Add this import
|
||||||
|
import '../widgets/video_player_widget.dart';
|
||||||
|
|
||||||
|
// The VideoScreen widget class
|
||||||
|
class VideoScreen extends StatefulWidget {
|
||||||
|
const VideoScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_VideoScreenState createState() => _VideoScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The State class for VideoScreen
|
||||||
|
class _VideoScreenState extends State<VideoScreen> {
|
||||||
|
String _videoUrl = '';
|
||||||
|
String _filePath = '';
|
||||||
|
String _subtitleUrl = ''; // Subtitle URL variable
|
||||||
|
String _subtitleFilePath = ''; // Subtitle file path
|
||||||
|
|
||||||
|
// Method to show video URL dialog
|
||||||
|
Future<void> _showVideoURLDialog() async {
|
||||||
|
final TextEditingController videoController = TextEditingController();
|
||||||
|
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('Enter Video URL'),
|
||||||
|
content: TextField(
|
||||||
|
controller: videoController,
|
||||||
|
decoration: InputDecoration(hintText: 'Enter a valid video URL'),
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('Cancel'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_videoUrl = videoController.text;
|
||||||
|
_filePath = '';
|
||||||
|
});
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to pick video file
|
||||||
|
Future<void> _pickFile() async {
|
||||||
|
FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.video);
|
||||||
|
|
||||||
|
if (result != null && result.files.single.path != null) {
|
||||||
|
setState(() {
|
||||||
|
_filePath = result.files.single.path!;
|
||||||
|
_videoUrl = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to pick subtitle file
|
||||||
|
Future<void> _pickSubtitleFile() async {
|
||||||
|
FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.custom, allowedExtensions: ['vtt']);
|
||||||
|
|
||||||
|
if (result != null && result.files.single.path != null) {
|
||||||
|
setState(() {
|
||||||
|
_subtitleFilePath = result.files.single.path!;
|
||||||
|
_subtitleUrl = ''; // Clear subtitle URL when a subtitle file is picked
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to enter subtitle URL
|
||||||
|
Future<void> _enterSubtitleURL() async {
|
||||||
|
final TextEditingController subtitleController = TextEditingController();
|
||||||
|
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('Enter Subtitle URL'),
|
||||||
|
content: TextField(
|
||||||
|
controller: subtitleController,
|
||||||
|
decoration: InputDecoration(hintText: 'Enter a valid subtitle URL'),
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('Cancel'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_subtitleUrl = subtitleController.text;
|
||||||
|
});
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: _videoUrl.isEmpty && _filePath.isEmpty
|
||||||
|
? AppBar(
|
||||||
|
title: Text('ExoPlayer Creator'),
|
||||||
|
)
|
||||||
|
: null, // Hide AppBar when video is playing
|
||||||
|
body: Center(
|
||||||
|
child: _videoUrl.isEmpty && _filePath.isEmpty
|
||||||
|
? Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _showVideoURLDialog,
|
||||||
|
child: Text('Enter Video URL'),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _pickFile,
|
||||||
|
child: Text('Choose Video File'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _enterSubtitleURL,
|
||||||
|
child: Text('Enter Subtitle URL'),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _pickSubtitleFile,
|
||||||
|
child: Text('Choose Subtitle File'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: VideoPlayerWidget(
|
||||||
|
videoUrl: _videoUrl,
|
||||||
|
filePath: _filePath,
|
||||||
|
subtitleUrl: _subtitleUrl, // Pass subtitle URL to VideoPlayerWidget
|
||||||
|
subtitleFilePath: _subtitleFilePath, // Pass subtitle file path to VideoPlayerWidget
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Future<void> requestPermissionIfNeeded(String subtitleFilePath, BuildContext context) async {
|
||||||
|
if (subtitleFilePath.isNotEmpty) {
|
||||||
|
// Only request permission if a subtitle file is chosen
|
||||||
|
PermissionStatus status = await Permission.storage.status;
|
||||||
|
|
||||||
|
if (!status.isGranted) {
|
||||||
|
// If permission is not granted, request it
|
||||||
|
status = await Permission.storage.request();
|
||||||
|
if (status.isDenied) {
|
||||||
|
// If permission is denied, show an alert dialog
|
||||||
|
_showPermissionDeniedDialog(context);
|
||||||
|
return; // Exit as we cannot proceed without permission
|
||||||
|
} else if (status.isPermanentlyDenied) {
|
||||||
|
// If the permission is permanently denied, guide the user to settings
|
||||||
|
_showPermissionDeniedDialog(context, permanentlyDenied: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPermissionDeniedDialog(BuildContext context, {bool permanentlyDenied = false}) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false, // Prevent tapping outside to dismiss
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('Permission Denied'),
|
||||||
|
content: Text(
|
||||||
|
permanentlyDenied
|
||||||
|
? 'The permission to access external storage has been permanently denied. Please go to the app settings to enable it.'
|
||||||
|
: 'You have denied the permission to access external storage. Please allow it to proceed.',
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (permanentlyDenied) {
|
||||||
|
// Optionally, open app settings if permission is permanently denied
|
||||||
|
openAppSettings();
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(); // Close the dialog
|
||||||
|
},
|
||||||
|
child: Text('OK'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
class WebVttCue {
|
||||||
|
final Duration start;
|
||||||
|
final Duration end;
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
WebVttCue({
|
||||||
|
required this.start,
|
||||||
|
required this.end,
|
||||||
|
required this.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WebVttCue> parseWebVtt(String subtitleData) {
|
||||||
|
final cuePattern = RegExp(r'(\d{2}:\d{2}:\d{2}.\d{3}) --> (\d{2}:\d{2}:\d{2}.\d{3})\n(.*?)\n\n', dotAll: true);
|
||||||
|
final List<WebVttCue> cues = [];
|
||||||
|
|
||||||
|
for (final match in cuePattern.allMatches(subtitleData)) {
|
||||||
|
final start = parseTime(match.group(1)!);
|
||||||
|
final end = parseTime(match.group(2)!);
|
||||||
|
final text = match.group(3)!;
|
||||||
|
cues.add(WebVttCue(start: start, end: end, text: text));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cues;
|
||||||
|
}
|
||||||
|
|
||||||
|
Duration parseTime(String time) {
|
||||||
|
final parts = time.split(':');
|
||||||
|
final secondsParts = parts[2].split('.');
|
||||||
|
return Duration(
|
||||||
|
hours: int.parse(parts[0]),
|
||||||
|
minutes: int.parse(parts[1]),
|
||||||
|
seconds: int.parse(secondsParts[0]),
|
||||||
|
milliseconds: int.parse(secondsParts[1]),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:video_player/video_player.dart';
|
||||||
|
import 'package:chewie/chewie.dart';
|
||||||
|
import 'dart:io'; // Import to use File class
|
||||||
|
import 'package:media3_exoplayer_creator/utils/permission_utils.dart'; // Import permission_utils.dart for permission handling
|
||||||
|
import 'package:media3_exoplayer_creator/utils/web_vtt.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class VideoPlayerWidget extends StatefulWidget {
|
||||||
|
final String videoUrl;
|
||||||
|
final String filePath;
|
||||||
|
final String subtitleUrl;
|
||||||
|
final String subtitleFilePath;
|
||||||
|
|
||||||
|
const VideoPlayerWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.videoUrl,
|
||||||
|
required this.filePath,
|
||||||
|
required this.subtitleUrl,
|
||||||
|
required this.subtitleFilePath,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_VideoPlayerWidgetState createState() => _VideoPlayerWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VideoPlayerWidgetState extends State<VideoPlayerWidget> {
|
||||||
|
late VideoPlayerController _videoPlayerController;
|
||||||
|
late ChewieController _chewieController;
|
||||||
|
late List<WebVttCue> _subtitles;
|
||||||
|
String? _currentSubtitle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// Request permission for subtitle files if a subtitle file path is provided
|
||||||
|
if (widget.subtitleFilePath.isNotEmpty) {
|
||||||
|
requestPermissionIfNeeded(widget.subtitleFilePath, context); // Correctly call the method here
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the video player controller based on video URL or file path
|
||||||
|
if (widget.filePath.isNotEmpty) {
|
||||||
|
_videoPlayerController = VideoPlayerController.file(File(widget.filePath));
|
||||||
|
} else {
|
||||||
|
_videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the Chewie controller for video playback
|
||||||
|
_chewieController = ChewieController(
|
||||||
|
videoPlayerController: _videoPlayerController,
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
autoPlay: true,
|
||||||
|
looping: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load subtitles if needed
|
||||||
|
_loadSubtitles();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to load subtitles (you can adapt it to your subtitle parsing logic)
|
||||||
|
Future<void> _loadSubtitles() async {
|
||||||
|
// Add your subtitle loading logic here
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Chewie(controller: _chewieController), // Video player widget
|
||||||
|
if (_currentSubtitle != null && _currentSubtitle!.isNotEmpty)
|
||||||
|
Positioned(
|
||||||
|
bottom: 50,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Text(
|
||||||
|
_currentSubtitle!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 19,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -129,6 +129,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
file_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: file_picker
|
||||||
|
sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.5.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -150,6 +158,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.24"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -304,6 +320,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.4.5"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.3.6"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.4"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.12.0"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -33,6 +33,8 @@ dependencies:
|
|||||||
video_player: ^2.3.0 # Make sure to check for the latest version
|
video_player: ^2.3.0 # Make sure to check for the latest version
|
||||||
chewie: ^1.2.2 # Optional, if you want to use a higher-level video player with controls
|
chewie: ^1.2.2 # Optional, if you want to use a higher-level video player with controls
|
||||||
keep_screen_on: ^3.0.0 # Add this line
|
keep_screen_on: ^3.0.0 # Add this line
|
||||||
|
file_picker: ^5.2.2 # Use the latest version
|
||||||
|
permission_handler: ^10.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -51,7 +53,7 @@ dev_dependencies:
|
|||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: true
|
android: true
|
||||||
ios: true
|
ios: true
|
||||||
image_path: "assets/icon/icon.png"
|
image_path: "assets/icon.png"
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
Loading…
Reference in New Issue
Block a user