Flutter视频下载插件awesome_video_downloader的使用

发布于 1周前 作者 ionicwang 来自 Flutter

Flutter视频下载插件awesome_video_downloader的使用

简介

awesome_video_downloader 是一个Flutter插件,用于下载各种格式(HLS、DASH、MP4)的视频,并支持后台下载、进度跟踪和离线播放。

特性

  • 支持多种视频格式:HLS、DASH、MP4等。
  • 并发下载。
  • 暂停、恢复和取消下载。
  • 实时进度跟踪。
  • 后台下载支持。
  • 跨平台(iOS & Android)。
  • 自适应流的质量选择。
  • 离线播放支持。

入门指南

安装

在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  awesome_video_downloader: ^1.0.0

平台设置

iOS

Info.plist 中添加以下键值:

<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
    <string>fetch</string>
</array>

Android

AndroidManifest.xml 中添加以下权限:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

使用方法

基本下载

final downloader = AwesomeVideoDownloader();
await downloader.initialize();

// 开始下载
final downloadId = await downloader.startDownload(
  url: 'https://example.com/video.mp4',
  fileName: 'my_video.mp4',
  format: 'mp4',
);

// 监控进度
downloader.getDownloadProgress(downloadId).listen(
  (progress) {
    print('Progress: ${progress.formattedProgress}');
    print('Speed: ${progress.formattedSpeed}');
    print('Downloaded: ${progress.formattedSize}');
    
    if (progress.isCompleted) {
      print('Download completed! File at: ${progress.filePath}');
    }
  },
  onError: (error) => print('Download error: $error'),
);

带选项的下载

final downloadId = await downloader.startDownload(
  url: 'https://example.com/stream.m3u8',
  fileName: 'video.mp4',
  format: 'hls',
  options: VideoDownloadOptions(
    minimumBitrate: 1500000,  // 1.5 Mbps
    maximumBitrate: 4000000,  // 4 Mbps
    preferHDR: true,
    preferMultichannel: true,
    headers: {
      'Authorization': 'Bearer token123',
    },
  ),
);

管理下载

// 暂停下载
await downloader.pauseDownload(downloadId);

// 恢复下载
await downloader.resumeDownload(downloadId);

// 取消下载
await downloader.cancelDownload(downloadId);

// 获取所有下载
final downloads = await downloader.getAllDownloads();
for (final download in downloads) {
  print('${download.fileName}: ${download.state.name}');
}

下载状态

下载可以处于以下状态:

  • notStarted: 下载尚未开始
  • downloading: 下载进行中
  • paused: 下载已暂停
  • completed: 下载已完成
  • failed: 下载失败
  • cancelled: 下载已取消

示例应用

以下是一个完整的示例应用程序,展示如何使用 awesome_video_downloader 插件:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:awesome_video_downloader/awesome_video_downloader.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Video Downloader Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const DownloaderPage(),
    );
  }
}

class DownloaderPage extends StatefulWidget {
  const DownloaderPage({super.key});

  @override
  State<DownloaderPage> createState() => _DownloaderPageState();
}

class _DownloaderPageState extends State<DownloaderPage> {
  final _downloader = AwesomeVideoDownloader();
  final _urlController = TextEditingController();
  final Map<String, StreamSubscription<DownloadProgress>> _progressSubscriptions = {};
  List<DownloadInfo> _downloads = [];

  @override
  void initState() {
    super.initState();
    _initializeDownloader();
  }

  Future<void> _initializeDownloader() async {
    try {
      await _downloader.initialize();
      _loadDownloads();
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Failed to initialize downloader: $e')),
        );
      }
    }
  }

  Future<void> _loadDownloads() async {
    _downloads = await _downloader.getAllDownloads();
    setState(() {});

    for (var download in _downloads) {
      if (download.isDownloading) {
        _subscribeToProgress(download.id);
      }
    }
  }

  void _subscribeToProgress(String downloadId) {
    _progressSubscriptions[downloadId]?.cancel();
    _progressSubscriptions[downloadId] =
        _downloader.getDownloadProgress(downloadId).listen(
      (progress) {
        setState(() {
          final index = _downloads.indexWhere((d) => d.id == downloadId);
          if (index != -1) {
            _downloads[index] = _downloads[index].copyWith(
              bytesDownloaded: progress.bytesDownloaded,
              totalBytes: progress.totalBytes,
              state: progress.state,
              filePath: progress.filePath,
            );

            if (progress.isCompleted) {
              _progressSubscriptions[downloadId]?.cancel();
              _progressSubscriptions.remove(downloadId);

              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text('Download completed: ${_downloads[index].fileName}'),
                  action: progress.filePath != null
                      ? SnackBarAction(
                          label: 'Show File',
                          onPressed: () {
                            // TODO: Implement file viewing
                          },
                        )
                      : null,
                ),
              );
            }
          }
        });
      },
      onError: (error) {
        setState(() {
          final index = _downloads.indexWhere((d) => d.id == downloadId);
          if (index != -1) {
            _downloads[index] = _downloads[index].copyWith(
              state: DownloadState.failed,
            );
          }
        });
        _progressSubscriptions[downloadId]?.cancel();
        _progressSubscriptions.remove(downloadId);
      },
    );
  }

  Future<void> _startDownload() async {
    if (_urlController.text.isEmpty) return;

    final url = _urlController.text;
    final uri = Uri.parse(url);
    final fileName = uri.pathSegments.last;

    try {
      final downloadId = await _downloader.startDownload(
        url: url,
        fileName: fileName,
        format: _getFormat(url),
        options: VideoDownloadOptions(
          preferHDR: true,
          preferMultichannel: true,
        ),
      );

      _subscribeToProgress(downloadId);
      _loadDownloads();
      _urlController.clear();
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Failed to start download: $e')),
        );
      }
    }
  }

  String _getFormat(String url) {
    if (url.contains('.m3u8')) return 'hls';
    if (url.contains('.mpd')) return 'dash';
    return 'mp4';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Video Downloader'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _urlController,
                    decoration: const InputDecoration(
                      hintText: 'Enter video URL',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 16),
                FilledButton(
                  onPressed: _startDownload,
                  child: const Text('Download'),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _downloads.length,
              itemBuilder: (context, index) {
                final download = _downloads[index];
                return DownloadListItem(
                  download: download,
                  onPause: () async {
                    await _downloader.pauseDownload(download.id);
                    _loadDownloads();
                  },
                  onResume: () async {
                    await _downloader.resumeDownload(download.id);
                    _subscribeToProgress(download.id);
                    _loadDownloads();
                  },
                  onCancel: () async {
                    await _downloader.cancelDownload(download.id);
                    _progressSubscriptions[download.id]?.cancel();
                    _progressSubscriptions.remove(download.id);
                    _loadDownloads();
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    for (var subscription in _progressSubscriptions.values) {
      subscription.cancel();
    }
    _urlController.dispose();
    super.dispose();
  }
}

class DownloadListItem extends StatelessWidget {
  final DownloadInfo download;
  final VoidCallback onPause;
  final VoidCallback onResume;
  final VoidCallback onCancel;

  const DownloadListItem({
    super.key,
    required this.download,
    required this.onPause,
    required this.onResume,
    required this.onCancel,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              download.fileName,
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 8),
            if (download.isDownloading) ...[
              LinearProgressIndicator(
                value: download.progress,
              ),
              const SizedBox(height: 8),
              Text(download.formattedSize),
            ] else
              Text('Status: ${download.state.name}'),
            if (download.filePath != null)
              Text('Saved at: ${download.filePath}',
                  style: Theme.of(context).textTheme.bodySmall),
            const SizedBox(height: 8),
            Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                if (download.isDownloading)
                  IconButton.filledTonal(
                    icon: const Icon(Icons.pause),
                    onPressed: onPause,
                  )
                else if (download.isPaused)
                  IconButton.filledTonal(
                    icon: const Icon(Icons.play_arrow),
                    onPressed: onResume,
                  ),
                const SizedBox(width: 8),
                IconButton.filledTonal(
                  icon: const Icon(Icons.cancel),
                  onPressed: onCancel,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

通过上述代码,您可以轻松地实现视频下载功能,并根据需要管理下载任务。希望这些信息对您有所帮助!


更多关于Flutter视频下载插件awesome_video_downloader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频下载插件awesome_video_downloader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用Flutter视频下载插件awesome_video_downloader的示例代码。这个插件允许你在Flutter应用中下载视频文件。

首先,确保你已经在pubspec.yaml文件中添加了awesome_video_downloader依赖:

dependencies:
  flutter:
    sdk: flutter
  awesome_video_downloader: ^x.y.z  # 替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter应用中,你可以使用以下代码来下载视频:

import 'package:flutter/material.dart';
import 'package:awesome_video_downloader/awesome_video_downloader.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Video Downloader Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _downloadStatus = 'Not started';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Video Downloader Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Download Status: $_downloadStatus',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _downloadVideo,
              child: Text('Download Video'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _downloadVideo() async {
    setState(() {
      _downloadStatus = 'Downloading...';
    });

    // 获取应用存储目录
    final Directory appDocDir = await getApplicationDocumentsDirectory();
    String appDocPath = appDocDir.path;

    // 视频URL(替换为你想要下载的视频URL)
    String videoUrl = 'https://www.example.com/path/to/your/video.mp4';

    // 设置下载路径
    String downloadPath = '$appDocPath/video.mp4';

    // 创建下载任务
    var downloader = VideoDownloader();

    try {
      var downloadId = await downloader.downloadFile(
        videoUrl,
        downloadPath,
      );

      // 检查下载状态(这里简单处理,实际应用中可能需要更复杂的状态管理)
      var status = await downloader.getDownloadStatus(downloadId);
      while (status.status != DownloadTaskStatus.complete) {
        setState(() {
          _downloadStatus = 'Downloaded: ${status.progressInPercent}%';
        });
        await Future.delayed(Duration(seconds: 1)); // 每秒更新一次进度
        status = await downloader.getDownloadStatus(downloadId);
      }

      setState(() {
        _downloadStatus = 'Download Complete!';
      });
    } catch (e) {
      setState(() {
        _downloadStatus = 'Download Failed: ${e.message}';
      });
    }
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个按钮用于启动视频下载任务。下载任务完成后,下载状态将更新为“Download Complete!”。如果下载失败,则会显示错误信息。

请注意:

  1. videoUrl需要替换为你想要下载的实际视频URL。
  2. 在实际应用中,你可能需要更复杂的错误处理和状态管理。
  3. awesome_video_downloader插件的API可能会有所变化,请参考最新的文档以获取最新信息。
回到顶部