mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 00:28:18 +08:00
feat: cdn speed test
Closes #105 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -764,6 +764,14 @@ List<SettingsModel> get videoSettings => [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
SettingsModel(
|
||||||
|
settingsType: SettingsType.sw1tch,
|
||||||
|
title: 'CDN 测速',
|
||||||
|
leading: const Icon(Icons.speed),
|
||||||
|
subtitle: '测速通过加载视频实现,注意流量消耗',
|
||||||
|
setKey: SettingBoxKey.cdnSpeedTest,
|
||||||
|
defaultVal: true,
|
||||||
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.sw1tch,
|
settingsType: SettingsType.sw1tch,
|
||||||
title: '音频不跟随 CDN 设置',
|
title: '音频不跟随 CDN 设置',
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
|
import 'package:PiliPalaX/http/video.dart';
|
||||||
|
import 'package:PiliPalaX/models/video/play/CDN.dart';
|
||||||
|
import 'package:PiliPalaX/models/video/play/url.dart';
|
||||||
|
import 'package:PiliPalaX/utils/extension.dart';
|
||||||
|
import 'package:PiliPalaX/utils/storage.dart';
|
||||||
|
import 'package:PiliPalaX/utils/video_utils.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get_utils/get_utils.dart';
|
||||||
|
|
||||||
class SelectDialog<T> extends StatefulWidget {
|
class SelectDialog<T> extends StatefulWidget {
|
||||||
final T value;
|
final T value;
|
||||||
@@ -17,11 +25,75 @@ class SelectDialog<T> extends StatefulWidget {
|
|||||||
|
|
||||||
class _SelectDialogState<T> extends State<SelectDialog<T>> {
|
class _SelectDialogState<T> extends State<SelectDialog<T>> {
|
||||||
late T _tempValue;
|
late T _tempValue;
|
||||||
|
late List _cdnResList;
|
||||||
|
late final cdnSpeedTest = GStorage.cdnSpeedTest;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tempValue = widget.value;
|
_tempValue = widget.value;
|
||||||
|
if (widget.title == 'CDN 设置' && cdnSpeedTest) {
|
||||||
|
_cdnResList = List.generate(widget.values.length, (_) => null);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
try {
|
||||||
|
dynamic result = await VideoHttp.videoUrl(
|
||||||
|
cid: 196018899,
|
||||||
|
bvid: 'BV1fK4y1t7hj',
|
||||||
|
);
|
||||||
|
if (result['status']) {
|
||||||
|
VideoItem videoItem = result['data'].dash.video.first;
|
||||||
|
|
||||||
|
for (CDNService item in CDNService.values) {
|
||||||
|
if (mounted.not) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String videoUrl = VideoUtils.getCdnUrl(videoItem, item.code);
|
||||||
|
Dio dio = Dio()
|
||||||
|
..options.headers['referer'] = 'https://www.bilibili.com/';
|
||||||
|
int maxSize = 8 * 1024 * 1024;
|
||||||
|
int downloaded = 0;
|
||||||
|
Stopwatch stopwatch = Stopwatch()..start();
|
||||||
|
try {
|
||||||
|
await dio.get(
|
||||||
|
videoUrl,
|
||||||
|
onReceiveProgress: (int count, int total) {
|
||||||
|
downloaded += count;
|
||||||
|
if (stopwatch.elapsedMilliseconds > 15 * 1000) {
|
||||||
|
stopwatch.stop();
|
||||||
|
dio.close(force: true);
|
||||||
|
}
|
||||||
|
if (downloaded >= maxSize) {
|
||||||
|
stopwatch.stop();
|
||||||
|
dio.close(force: true);
|
||||||
|
_cdnResList[item.index] =
|
||||||
|
(maxSize / stopwatch.elapsedMilliseconds / 1000)
|
||||||
|
.toPrecision(2);
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
stopwatch.stop();
|
||||||
|
if (_cdnResList[item.index] == null) {
|
||||||
|
_cdnResList[item.index] = '测速失败: $e';
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stopwatch.isRunning) {
|
||||||
|
stopwatch.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('failed to check: $e');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -39,22 +111,31 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
|
|||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: List.generate(
|
||||||
for (var i in widget.values) ...[
|
widget.values.length,
|
||||||
RadioListTile(
|
(index) => RadioListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
value: i['value'],
|
value: widget.values[index]['value'],
|
||||||
title: Text(i['title'], style: titleStyle),
|
title: Text(widget.values[index]['title'], style: titleStyle),
|
||||||
groupValue: _tempValue,
|
subtitle: widget.title == 'CDN 设置' && cdnSpeedTest
|
||||||
onChanged: (value) {
|
? Text(
|
||||||
setState(() {
|
_cdnResList[index] is double
|
||||||
_tempValue = value as T;
|
? '${_cdnResList[index]} MB/s'
|
||||||
});
|
: _cdnResList[index] is String
|
||||||
Navigator.pop(context, _tempValue);
|
? _cdnResList[index]
|
||||||
},
|
: '---',
|
||||||
),
|
style: TextStyle(fontSize: 13),
|
||||||
]
|
)
|
||||||
],
|
: null,
|
||||||
|
groupValue: _tempValue,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_tempValue = value as T;
|
||||||
|
});
|
||||||
|
Navigator.pop(context, _tempValue);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -287,6 +287,9 @@ class GStorage {
|
|||||||
static bool get continuePlayingPart =>
|
static bool get continuePlayingPart =>
|
||||||
setting.get(SettingBoxKey.continuePlayingPart, defaultValue: true);
|
setting.get(SettingBoxKey.continuePlayingPart, defaultValue: true);
|
||||||
|
|
||||||
|
static bool get cdnSpeedTest =>
|
||||||
|
setting.get(SettingBoxKey.cdnSpeedTest, defaultValue: true);
|
||||||
|
|
||||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||||
|
|
||||||
@@ -495,6 +498,7 @@ class SettingBoxKey {
|
|||||||
subtitleBgOpaticy = 'subtitleBgOpaticy',
|
subtitleBgOpaticy = 'subtitleBgOpaticy',
|
||||||
badCertificateCallback = 'badCertificateCallback',
|
badCertificateCallback = 'badCertificateCallback',
|
||||||
continuePlayingPart = 'continuePlayingPart',
|
continuePlayingPart = 'continuePlayingPart',
|
||||||
|
cdnSpeedTest = 'cdnSpeedTest',
|
||||||
|
|
||||||
// Sponsor Block
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ class VideoUtils {
|
|||||||
RegExp(r'^https?://\d{1,3}\.\d{1,3}').hasMatch(url);
|
RegExp(r'^https?://\d{1,3}\.\d{1,3}').hasMatch(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getCdnUrl(dynamic item) {
|
static String getCdnUrl(dynamic item, [defaultCDNService]) {
|
||||||
String? backupUrl;
|
String? backupUrl;
|
||||||
String? videoUrl;
|
String? videoUrl;
|
||||||
String defaultCDNService = GStorage.setting
|
defaultCDNService ??= GStorage.setting
|
||||||
.get(SettingBoxKey.CDNService, defaultValue: CDNService.backupUrl.code);
|
.get(SettingBoxKey.CDNService, defaultValue: CDNService.backupUrl.code);
|
||||||
if (item is AudioItem) {
|
if (item is AudioItem) {
|
||||||
if (GStorage.setting
|
if (GStorage.setting
|
||||||
|
|||||||
Reference in New Issue
Block a user