初始化
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# m3u8_downloader_example
|
||||
|
||||
Demonstrates how to use the m3u8_downloader plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@@ -0,0 +1,58 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
// throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 32
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.vincent.m3u8_downloader_example"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 31
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
dependencies {
|
||||
|
||||
implementation 'com.android.support:multidex:1.0.3' //加这个
|
||||
|
||||
}
|
||||
flutter {
|
||||
|
||||
source '../..'
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.vincent.m3u8_downloader_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,49 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.vincent.m3u8_downloader_example">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<application
|
||||
android:label="m3u8_downloader_example"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:exported="true">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.vincent.m3u8_downloader_example;
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.vincent.m3u8_downloader_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,27 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#Fri Jun 23 08:50:38 CEST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
@@ -0,0 +1,11 @@
|
||||
include ':app'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
@@ -0,0 +1 @@
|
||||
{"inputs":[],"outputs":[]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"inputs":["/Users/mac/Documents/workspace/hjllnew/m3u8_downloader-master/example/.dart_tool/package_config_subset"],"outputs":["/Users/mac/Documents/workspace/hjllnew/m3u8_downloader-master/example/.dart_tool/flutter_build/dart_plugin_registrant.dart"]}
|
||||
@@ -0,0 +1,224 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:m3u8_downloader/m3u8_downloader.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import 'video_player_page.dart';
|
||||
|
||||
void main() => runApp(MyApp());
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
@override
|
||||
_MyAppState createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
ReceivePort _port = ReceivePort();
|
||||
String? _downloadingUrl;
|
||||
|
||||
//
|
||||
//http://m3u8.afhklsdd.com/d1af20263f1b11cc685ba52e842f6659.m3u8
|
||||
// 未加密的url地址(喜羊羊与灰太狼之决战次时代)
|
||||
//String url1 = "http://m3u8.afhklsdd.com/2eb056a7a1fc2774aa536421939d73a49652eac7.m3u8"; //"https://cdn.605-zy.com/20210713/MiJecHrZ/index.m3u8";
|
||||
// 加密的url地址(火影忍者疾风传)
|
||||
//String url2 = "https://v3.dious.cc/20201116/SVGYv7Lo/index.m3u8";
|
||||
String url2 =
|
||||
"http://163.53.216.122:9898/api/media/m3u8/sp/ah/a9/nk/pr/441c94f4f57b4b1a9058217eaf3663d3.m3u8?Authorization=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJwdWJsaWMiLCJleHAiOjE2OTQzNDIzMTEsImlzc3VlciI6ImNvbS5idXR0ZXJmbHkiLCJzdWIiOiJhc2lnbiIsInVzZXJJZCI6OTk4MzMxMn0.u98UciEaat-JYiSx5GueWtYMM8gt61TSBy43maBdCvI"; // "http://m3u8.afhklsdd.com/7266e82a230a18ef457fe82b6c853fad.m3u8";
|
||||
|
||||
//"http://m3u8.afhklsdd.com/7266e82a230a18ef457fe82b6c853fad.m3u8";
|
||||
//"https://m3u8.afhklsdd.com/fd1ff0da268f722901030204c0c1edbf.m3u8";//"http://m3u8.afhklsdd.com/d1af20263f1b11cc685ba52e842f6659.m3u8";
|
||||
dynamic taskInfo;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
initAsync();
|
||||
});
|
||||
}
|
||||
|
||||
dynamic progress;
|
||||
|
||||
void initAsync() async {
|
||||
String saveDir = await _findSavePath();
|
||||
print(saveDir);
|
||||
bool result = await M3u8Downloader.initialize(onSelect: () async {
|
||||
print('下载成功点击');
|
||||
return null;
|
||||
});
|
||||
print("initialize====$result");
|
||||
await M3u8Downloader.config(
|
||||
saveDir: saveDir,
|
||||
threadCount: 5,
|
||||
convertMp4: true,
|
||||
debugMode: true,
|
||||
progressCallback: progressCallback,
|
||||
successCallback: successCallback,
|
||||
errorCallback: errorCallback,
|
||||
);
|
||||
// 注册监听器
|
||||
IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
|
||||
_port.listen((dynamic data) {
|
||||
// 监听数据请求
|
||||
print(data);
|
||||
progress = data;
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> _checkPermission() async {
|
||||
var status = await Permission.storage.status;
|
||||
if (!status.isGranted) {
|
||||
status = await Permission.storage.request();
|
||||
}
|
||||
return status.isGranted;
|
||||
}
|
||||
|
||||
Future<String> _findSavePath() async {
|
||||
var directory = await getExternalStorageDirectory(); //getApplicationSupportDirectory();
|
||||
String saveDir = "/sdcard/vPlayDownload"; //directory!.path + '/vPlayDownload';
|
||||
Directory root = Directory(saveDir);
|
||||
if (!root.existsSync()) {
|
||||
await root.create();
|
||||
}
|
||||
print(saveDir);
|
||||
return saveDir;
|
||||
}
|
||||
|
||||
static progressCallback(dynamic args) {
|
||||
//print("progressCallback====$args");
|
||||
final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');
|
||||
if (send != null) {
|
||||
args["status"] = 1;
|
||||
send.send(args);
|
||||
}
|
||||
}
|
||||
|
||||
static successCallback(dynamic args) {
|
||||
print("successCallback====$args");
|
||||
final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');
|
||||
if (send != null) {
|
||||
send.send({"status": 2, "url": args["url"], "filePath": args["filePath"], "dir": args["dir"]});
|
||||
}
|
||||
}
|
||||
|
||||
static errorCallback(dynamic args) {
|
||||
print("errorCallback====$args");
|
||||
final SendPort? send = IsolateNameServer.lookupPortByName('downloader_send_port');
|
||||
if (send != null) {
|
||||
send.send({"status": 3, "url": args["url"]});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
useInheritedMediaQuery: true,
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Plugin example app'),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
child: Text("${_downloadingUrl == url2 ? '暂停' : '下载'}已加密m3u8"),
|
||||
onPressed: () {
|
||||
if (_downloadingUrl == url2) {
|
||||
// 暂停
|
||||
setState(() {
|
||||
_downloadingUrl = null;
|
||||
});
|
||||
M3u8Downloader.pause(url2);
|
||||
return;
|
||||
}
|
||||
// 下载
|
||||
_checkPermission().then((hasGranted) async {
|
||||
if (hasGranted) {
|
||||
await M3u8Downloader.config(
|
||||
convertMp4: true,
|
||||
);
|
||||
setState(() {
|
||||
_downloadingUrl = url2;
|
||||
});
|
||||
final result = await M3u8Downloader.download(
|
||||
url: url2,
|
||||
name: "下载已加密m3u8",
|
||||
);
|
||||
print("=========download:$result");
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
_downloadingUrl = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text("打开已下载的已加密的文件"),
|
||||
onPressed: () async {
|
||||
final result = await M3u8Downloader.download(
|
||||
url: url2,
|
||||
name: "下载已加密m3u8",
|
||||
progressCallback: progressCallback,
|
||||
successCallback: successCallback,
|
||||
errorCallback: errorCallback);
|
||||
print("=========download:$result");
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
_downloadingUrl = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text("清空下载"),
|
||||
onPressed: () async {
|
||||
await M3u8Downloader.delete(url2);
|
||||
print("清理完成");
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text(taskInfo?.toString() ?? "查询"),
|
||||
onPressed: () async {
|
||||
taskInfo = await M3u8Downloader.searchInfo(url2);
|
||||
print("=======taskInfo:");
|
||||
print(taskInfo);
|
||||
print("=======taskInfo:");
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
Container(
|
||||
child: Text(progress?.toString() ?? "progress"),
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text("movie"),
|
||||
onPressed: () {
|
||||
print("Navigator push:${context}");
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => VideoM38UPlayerPage(),
|
||||
),
|
||||
);
|
||||
// Navigator.of(context).push(MaterialPageRoute(builder: (context){
|
||||
// print("Navigator push VideoPlayerPage");
|
||||
// return VideoPlayerPage();
|
||||
// }));
|
||||
},
|
||||
),
|
||||
Container(
|
||||
height: 300,
|
||||
width: 400,
|
||||
child: VideoM38UPlayerPage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:m3u8_downloader/m3u8_downloader.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
//视频播放器封装需要使用动态类
|
||||
class VideoM38UPlayerPage extends StatefulWidget {
|
||||
@override
|
||||
_VideoPlayerPageState createState() => _VideoPlayerPageState();
|
||||
}
|
||||
|
||||
//继承VideoApp类
|
||||
class _VideoPlayerPageState extends State<VideoM38UPlayerPage> {
|
||||
|
||||
//定义一个VideoPlayerController
|
||||
VideoPlayerController? _controller;
|
||||
|
||||
//重写类方法initState(),初始化界面
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
print("_VideoPlayerPageState init");
|
||||
//设置视频参数 (..)是级联的意思
|
||||
// initController();
|
||||
}
|
||||
|
||||
|
||||
void initController() {
|
||||
String url = "http://163.53.216.122:9898/api/media/m3u8/sp/ah/a9/nk/pr/441c94f4f57b4b1a9058217eaf3663d3.m3u8?Authorization=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJwdWJsaWMiLCJleHAiOjE2OTQzNDIzMTEsImlzc3VlciI6ImNvbS5idXR0ZXJmbHkiLCJzdWIiOiJhc2lnbiIsInVzZXJJZCI6OTk4MzMxMn0.u98UciEaat-JYiSx5GueWtYMM8gt61TSBy43maBdCvI"; //"""https://m3u8.afhklsdd.com/5678954c76ece8b4345ae437e7344367e22e2G1o.m3u8";
|
||||
M3u8Downloader.searchInfo(url).then((result) {
|
||||
if (result is Map) {
|
||||
Map fileInfoMap = result;
|
||||
String? filePath = fileInfoMap["localPath"];
|
||||
// filePath = filePath?.replaceAll("local.m3u8", "remote.m3u8");
|
||||
print(filePath);
|
||||
if (filePath?.isNotEmpty == true) {
|
||||
|
||||
_controller = VideoPlayerController.file(File(filePath!))
|
||||
..initialize().then((value) {
|
||||
setState(() {
|
||||
if (_controller == null) {
|
||||
_controller?.addListener(() {setState(() {
|
||||
|
||||
});});
|
||||
print("_controller success local path");
|
||||
_controller?.play();
|
||||
}
|
||||
});
|
||||
});
|
||||
}else {
|
||||
_controller = VideoPlayerController.network(url)..initialize().then((value) {
|
||||
setState(() {
|
||||
if (_controller == null) {
|
||||
_controller?.addListener(() {setState(() {
|
||||
|
||||
});});
|
||||
print("_controller success");
|
||||
_controller?.play();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Text("back"),
|
||||
),
|
||||
body: Center(
|
||||
child: (_controller?.value.isInitialized ?? false)
|
||||
? AspectRatio(
|
||||
aspectRatio: _controller!.value.aspectRatio,
|
||||
child: VideoPlayer(_controller!),
|
||||
)
|
||||
: Container(
|
||||
child: Text("没有要播放的视频"),
|
||||
),
|
||||
),
|
||||
|
||||
//右下角图标按钮onPressed中需要调用setState方法,用于刷新界面
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
if (_controller != null) {
|
||||
_controller!.value.isPlaying
|
||||
? _controller!.pause() : _controller!.play();
|
||||
}else{
|
||||
initController();
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
(_controller?.value.isPlaying ?? false)? Icons.pause : Icons.play_arrow,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//dispose():程序中是用来关闭一个GUI页面的
|
||||
//视频播放完需要把页面关闭
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_controller?.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
name: m3u8_downloader_example
|
||||
description: Demonstrates how to use the m3u8_downloader plugin.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
path_provider: ^2.0.2
|
||||
permission_handler: ^8.1.4+2
|
||||
open_file: ^3.2.1
|
||||
m3u8_downloader:
|
||||
# When depending on this package from a real application you should use:
|
||||
# m3u8_downloader: ^x.y.z
|
||||
# See https://dart.dev/tools_base/pub/dependencies#version-constraints
|
||||
# The example app is bundled with the plugin so we use a path dependency on
|
||||
# the parent directory to use the current plugin's version.
|
||||
path: ../
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
video_player: ^2.2.19
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools_base/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
@@ -0,0 +1,26 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../lib/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(Widget widget) => widget is Text && widget.data!.startsWith('Running on:'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user