ToneSnap_FSX_Flutter/lib/global/download_manager.dart
fengshengxiong da21720c3c 1.首页增加下拉刷新
2.修改下载状态监听方式,实现全局同步
3.修复搜索无结果时页面报错
4.歌单页面点击播放全部和随机是播放当前歌单列表
2024-08-06 15:52:07 +08:00

179 lines
6.5 KiB
Dart

// Author: fengshengxiong
// Date: 2024/5/10
// Description: 下载管理
import 'package:background_downloader/background_downloader.dart';
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:tone_snap/components/base_easyloading.dart';
import 'package:tone_snap/data/api/music_api.dart';
import 'package:tone_snap/data/models/music_model.dart';
import 'package:tone_snap/data/models/player_model.dart';
import 'package:tone_snap/data/storage/offline_box.dart';
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
import 'package:tone_snap/utils/date_util.dart';
import 'package:tone_snap/utils/local_path_util.dart';
import 'package:tone_snap/utils/log_util.dart';
import 'package:tone_snap/utils/obj_util.dart';
class DownloadManager extends GetxController {
static DownloadManager get to => Get.put(DownloadManager(), permanent: true);
final downloadStateId = 'download_state_id';
MemoryTaskQueue? tq;
List<MusicModel> downloadList = [];
@override
void onInit() {
super.onInit();
if (tq == null) {
tq ??= MemoryTaskQueue();
tq!.maxConcurrent = 3; // no more than 5 tasks active at any one time
tq!.maxConcurrentByHost = 3; // no more than two tasks talking to the same host at the same time
tq!.maxConcurrentByGroup = 3; // no more than three tasks from the same group active at the same time
FileDownloader().addTaskQueue(tq!); // 'connects' the TaskQueue to the FileDownloader
FileDownloader().updates.listen((update) async { // listen to updates as per usual
MusicModel? musicModel = downloadList.firstWhereOrNull((e) => e.videoId == update.task.taskId);
if (musicModel == null) return;
if (update.runtimeType == TaskStatusUpdate) {
TaskStatus taskStatus = (update as TaskStatusUpdate).status;
LogUtil.d('${update.task.filename},任务状态: $taskStatus');
musicModel.taskStatus = taskStatus;
switch (taskStatus) {
case TaskStatus.enqueued:
break;
case TaskStatus.running:
break;
case TaskStatus.complete:
LogUtil.d('音乐下载路径:${await update.task.filePath()}');
musicModel.localPath = await update.task.filePath();
OfflineBox().add(musicModel);
downloadList.remove(musicModel);
BaseEasyLoading.toast('Download completed');
if (Get.isRegistered<PersonalMusicLibraryController>()) {
PersonalMusicLibraryController.to.refreshOffline();
}
if (Get.isRegistered<OfflineController>()) {
OfflineController.to.getOfflineList();
}
break;
case TaskStatus.notFound:
BaseEasyLoading.toast('Download failed');
downloadList.remove(musicModel);
break;
case TaskStatus.failed:
BaseEasyLoading.toast('Download failed');
downloadList.remove(musicModel);
break;
case TaskStatus.canceled:
BaseEasyLoading.toast('Download cancelled');
downloadList.remove(musicModel);
break;
case TaskStatus.waitingToRetry:
break;
case TaskStatus.paused:
break;
}
}
if (update.runtimeType == TaskProgressUpdate) {
LogUtil.d('${update.task.filename},下载进度: $update');
musicModel.progress = (update as TaskProgressUpdate).progress;
}
updateDownloadState();
});
}
}
/// 下载文件
void downloadMusic(MusicModel? m) {
if (m == null) return;
MusicModel musicModel = m.copyWith();
musicModel.taskStatus = TaskStatus.enqueued;
musicModel.cancelToken = CancelToken();
downloadList.add(musicModel);
updateDownloadState();
_getMusicUrl(musicModel, (url, mimeType) async {
if (ObjUtil.isNotEmpty(url)) {
String extension = mimeType ?? 'mp4';
if (ObjUtil.isNotEmpty(mimeType)) {
// 从 mimeType 中提取主类型和子类型
String type = mimeType!.split(';')[0].trim();
// 获取文件扩展名
extension = type.split('/')[1];
}
final filename = '${musicModel.title}_${DateUtil.getNowTimestamp()}.$extension';
final task = DownloadTask(
taskId: musicModel.videoId,
url: url!,
filename: filename,
directory: LocalPathUtil.getMusicDownloadDir(),
updates: Updates.statusAndProgress,
requiresWiFi: false,
retries: 0,
allowPause: false,
metaData: '',
);
tq?.add(task);
}
});
}
Future<void> _getMusicUrl(MusicModel musicModel, Function(String? url, String? mimeType) onTap) async {
PlayerModel? playerModel = await MusicApi.player(
videoId: musicModel.videoId,
cancelToken: musicModel.cancelToken,
fail: (baseError) {
if (baseError.code == DioExceptionType.cancel.index) {
musicModel.taskStatus = TaskStatus.canceled;
musicModel.cancelToken = null;
} else {
musicModel.taskStatus = TaskStatus.failed;
musicModel.cancelToken = null;
}
downloadList.remove(musicModel);
updateDownloadState();
}
);
if (playerModel != null) {
if (ObjUtil.isEmpty(musicModel.coverUrl)) {
var thumbnails = playerModel.videoDetails?.thumbnail?.thumbnails;
if (thumbnails != null && thumbnails.isNotEmpty) {
musicModel.coverUrl = thumbnails.last.url;
}
}
if (ObjUtil.isEmpty(musicModel.musicType)) {
musicModel.musicType = playerModel.videoDetails?.musicVideoType;
}
var formats = playerModel.streamingData?.formats;
if (formats != null && formats.isNotEmpty) {
onTap(formats[0].url, formats[0].mimeType);
}
}
}
void cancelDownload(String? videoId) {
if (ObjUtil.isEmpty(videoId)) return;
final m = getMusicModel(videoId);
if (m != null) {
m.cancelToken?.cancel();
if (m.videoId != null) {
FileDownloader().cancelTaskWithId(m.videoId!);
}
}
}
void updateDownloadState() {
DownloadManager.to.update([downloadStateId]);
}
MusicModel? getMusicModel(String? videoId) {
if (ObjUtil.isEmpty(videoId)) return null;
final m = downloadList.firstWhereOrNull((e) => e.videoId == videoId);
return m;
}
}