feat: cdn speed test

Closes #105

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-01-07 10:08:39 +08:00
parent d9474a79c1
commit f5b50ffcb0
4 changed files with 111 additions and 18 deletions

View File

@@ -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 设置',

View File

@@ -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);
},
),
),
), ),
); );
}), }),

View File

@@ -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',

View File

@@ -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