mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-05 09:37:52 +08:00
feat: video download
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -25,15 +25,18 @@ import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart';
|
||||
import 'package:PiliPlus/pages/setting/widgets/slide_dialog.dart';
|
||||
import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||
import 'package:PiliPlus/services/download/download_service.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/cache_manage.dart';
|
||||
import 'package:PiliPlus/utils/cache_manager.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/path_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/update.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -43,7 +46,7 @@ import 'package:get/get.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
|
||||
List<SettingsModel> get extraSettings => [
|
||||
if (Utils.isDesktop)
|
||||
if (Utils.isDesktop) ...[
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
title: '退出时最小化',
|
||||
@@ -56,6 +59,63 @@ List<SettingsModel> get extraSettings => [
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
title: '缓存路径',
|
||||
getSubtitle: () => downloadPath,
|
||||
leading: const Icon(Icons.storage),
|
||||
onTap: (setState) {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.copyText(downloadPath);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('复制', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
final defPath = defDownloadPath;
|
||||
if (downloadPath == defPath) return;
|
||||
downloadPath = defPath;
|
||||
setState();
|
||||
Get.find<DownloadService>().readDownloadList();
|
||||
GStorage.setting.delete(SettingBoxKey.downloadPath);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('重置', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
Get.back();
|
||||
final path = await FilePicker.platform.getDirectoryPath();
|
||||
if (path == null || path == downloadPath) return;
|
||||
downloadPath = path;
|
||||
setState();
|
||||
Get.find<DownloadService>().readDownloadList();
|
||||
GStorage.setting.put(SettingBoxKey.downloadPath, path);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('设置新路径', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
title: '空降助手',
|
||||
@@ -1104,7 +1164,7 @@ List<SettingsModel> get extraSettings => [
|
||||
title: '最大缓存大小',
|
||||
getSubtitle: () {
|
||||
final num = Pref.maxCacheSize;
|
||||
return '当前最大缓存大小: 「${num == 0 ? '无限' : CacheManage.formatSize(Pref.maxCacheSize)}」';
|
||||
return '当前最大缓存大小: 「${num == 0 ? '无限' : CacheManager.formatSize(Pref.maxCacheSize)}」';
|
||||
},
|
||||
onTap: (setState) {
|
||||
showDialog(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/ua_type.dart';
|
||||
import 'package:PiliPlus/http/video.dart';
|
||||
import 'package:PiliPlus/models/common/video/cdn_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/video_type.dart';
|
||||
@@ -80,18 +81,19 @@ class CdnSelectDialog extends StatefulWidget {
|
||||
|
||||
class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
late final List<ValueNotifier<String?>> _cdnResList;
|
||||
late final CancelToken _cancelToken;
|
||||
late final List<CancelToken?> _tokens;
|
||||
late final bool _cdnSpeedTest;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_cdnSpeedTest = Pref.cdnSpeedTest;
|
||||
if (_cdnSpeedTest) {
|
||||
final length = CDNService.values.length;
|
||||
_cdnResList = List.generate(
|
||||
CDNService.values.length,
|
||||
length,
|
||||
(_) => ValueNotifier<String?>(null),
|
||||
);
|
||||
_cancelToken = CancelToken();
|
||||
_tokens = List.generate(length, (_) => CancelToken());
|
||||
_startSpeedTest();
|
||||
}
|
||||
super.initState();
|
||||
@@ -100,10 +102,13 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
@override
|
||||
void dispose() {
|
||||
if (_cdnSpeedTest) {
|
||||
_cancelToken.cancel();
|
||||
for (final e in _tokens) {
|
||||
e?.cancel();
|
||||
}
|
||||
for (final notifier in _cdnResList) {
|
||||
notifier.dispose();
|
||||
}
|
||||
_dio.close(force: true);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
@@ -145,26 +150,38 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
late final _dio = Dio()
|
||||
..options.headers = {
|
||||
'user-agent': UaType.pc.ua,
|
||||
'referer': HttpString.baseUrl,
|
||||
};
|
||||
|
||||
Future<void> _measureDownloadSpeed(String url, int index) async {
|
||||
const maxSize = 8 * 1024 * 1024;
|
||||
int downloaded = 0;
|
||||
final dio = Dio()..options.headers['referer'] = HttpString.baseUrl;
|
||||
|
||||
final cancelToken = _tokens[index];
|
||||
final start = DateTime.now().microsecondsSinceEpoch;
|
||||
|
||||
await dio.get(
|
||||
void onClose() {
|
||||
cancelToken?.cancel();
|
||||
_tokens[index] = null;
|
||||
}
|
||||
|
||||
await _dio.get(
|
||||
url,
|
||||
cancelToken: _cancelToken,
|
||||
cancelToken: cancelToken,
|
||||
onReceiveProgress: (count, total) {
|
||||
if (!mounted) {
|
||||
dio.close(force: true);
|
||||
return;
|
||||
}
|
||||
|
||||
final duration = DateTime.now().microsecondsSinceEpoch - start;
|
||||
|
||||
downloaded += count;
|
||||
|
||||
if (duration > 15000000) {
|
||||
dio.close(force: true);
|
||||
onClose();
|
||||
if (downloaded > 0) {
|
||||
_updateSpeedResult(index, downloaded, duration);
|
||||
downloaded = 0;
|
||||
@@ -172,7 +189,7 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
throw TimeoutException('测速超时');
|
||||
}
|
||||
} else if (downloaded >= maxSize) {
|
||||
dio.close(force: true);
|
||||
onClose();
|
||||
_updateSpeedResult(index, downloaded, duration);
|
||||
downloaded = 0;
|
||||
}
|
||||
@@ -186,6 +203,9 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
}
|
||||
|
||||
void _handleSpeedTestError(dynamic error, int index) {
|
||||
_tokens
|
||||
..[index]?.cancel()
|
||||
..[index] = null;
|
||||
final item = _cdnResList[index];
|
||||
if (item.value != null) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user