feat: 新增更多CDN选项,且默认单独关闭音频CDN

This commit is contained in:
orz12
2024-07-20 19:34:06 +08:00
parent 71edc77dc6
commit 97ea882166
6 changed files with 250 additions and 47 deletions

View File

@@ -0,0 +1,114 @@
//https://github.com/yujincheng08/BiliRoaming/blob/master/app/src/main/res/values/strings_raw.xml
//https://github.com/yujincheng08/BiliRoaming/blob/master/app/src/main/res/values/arrays.xml
enum CDNService {
baseUrl,
backupUrl,
ali,
alib,
alio1,
cos,
cosb,
coso1,
hw,
hwb,
hwo1,
hw_08c,
hw_08h,
hw_08ct,
tf_hw,
tf_tx,
akamai,
aliov,
cosov,
hwov,
hk_bcache,
}
extension CDNServiceDesc on CDNService {
static final List<String> _descList = [
'基础 URL不推荐',
'备用 URL',
'ali阿里云',
'alib阿里云',
'alio1阿里云',
'cos腾讯云',
'cosb腾讯云VOD 加速类型)',
'coso1腾讯云',
'hw华为云融合 CDN',
'hwb华为云融合 CDN',
'hwo1华为云融合 CDN',
'08c华为云融合 CDN',
'08h华为云融合 CDN',
'08ct华为云融合 CDN',
'tf_hw华为云',
'tf_tx腾讯云',
'akamaiAkamai 海外)',
'aliov阿里云海外',
'cosov腾讯云海外',
'hwov华为云海外',
'hk_bcacheBilibili海外',
];
String get description => _descList[index];
}
extension CDNServiceHost on CDNService {
static final List<String> _hostList = [
'',
'',
'upos-sz-mirrorali.bilivideo.com',
'upos-sz-mirroralib.bilivideo.com',
'upos-sz-mirroralio1.bilivideo.com',
'upos-sz-mirrorcos.bilivideo.com',
'upos-sz-mirrorcosb.bilivideo.com',
'upos-sz-mirrorcoso1.bilivideo.com',
'upos-sz-mirrorhw.bilivideo.com',
'upos-sz-mirrorhwb.bilivideo.com',
'upos-sz-mirrorhwo1.bilivideo.com',
'upos-sz-mirror08c.bilivideo.com',
'upos-sz-mirror08h.bilivideo.com',
'upos-sz-mirror08ct.bilivideo.com',
'upos-tf-all-hw.bilivideo.com',
'upos-tf-all-tx.bilivideo.com',
'upos-hz-mirrorakam.akamaized.net',
'upos-sz-mirroraliov.bilivideo.com',
'upos-sz-mirrorcosov.bilivideo.com',
'upos-sz-mirrorhwov.bilivideo.com',
'cn-hk-eq-bcache-01.bilivideo.com',
];
String get host => _hostList[index];
}
extension CDNServiceCode on CDNService {
static final List<String> _codeList = [
'baseUrl',
'backupUrl',
'ali',
'alib',
'alio1',
'cos',
'cosb',
'coso1',
'hw',
'hwb',
'hwo1',
'hw_08c',
'hw_08h',
'hw_08ct',
'tf_hw',
'tf_tx',
'akamai',
'aliov',
'cosov',
'hwov',
'hk_bcache',
];
String get code => _codeList[index];
static CDNService? fromCode(String code) {
final index = _codeList.indexOf(code);
if (index != -1) {
return CDNService.values[index];
}
return null;
}
}

View File

@@ -18,7 +18,7 @@ class LiveRoomController extends GetxController {
PlPlayerController plPlayerController =
PlPlayerController.getInstance(videoType: 'live');
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
late bool enableCDN;
// late bool enableCDN;
@override
void onInit() {
@@ -35,7 +35,7 @@ class LiveRoomController extends GetxController {
}
}
// CDN优化
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
// enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
}
playerInit(source) async {
@@ -62,11 +62,7 @@ class LiveRoomController extends GetxController {
List<CodecItem> codec =
res['data'].playurlInfo.playurl.stream.first.format.first.codec;
CodecItem item = codec.first;
String videoUrl = enableCDN
? VideoUtils.getCdnUrl(item)
: (item.urlInfo?.first.host)! +
item.baseUrl! +
item.urlInfo!.first.extra!;
String videoUrl = VideoUtils.getCdnUrl(item);
await playerInit(videoUrl);
return res;
}

View File

@@ -3,8 +3,10 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:PiliPalaX/models/video/play/quality.dart';
import 'package:PiliPalaX/models/video/play/CDN.dart';
import 'package:PiliPalaX/pages/setting/widgets/select_dialog.dart';
import 'package:PiliPalaX/utils/storage.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'widgets/switch_item.dart';
@@ -23,6 +25,7 @@ class _VideoSettingState extends State<VideoSetting> {
late dynamic secondDecode;
late dynamic hardwareDecoding;
late dynamic videoSync;
late dynamic defaultCDNService;
@override
void initState() {
@@ -39,6 +42,8 @@ class _VideoSettingState extends State<VideoSetting> {
defaultValue: Platform.isAndroid ? 'auto-safe' : 'auto');
videoSync =
setting.get(SettingBoxKey.videoSync, defaultValue: 'display-resample');
defaultCDNService = setting.get(SettingBoxKey.CDNService,
defaultValue: CDNService.ali.code);
}
@override
@@ -80,11 +85,37 @@ class _VideoSettingState extends State<VideoSetting> {
setKey: SettingBoxKey.p1080,
defaultVal: true,
),
const SetSwitchItem(
title: 'CDN优化',
subTitle: '使用优质CDN线路',
leading: Icon(Icons.network_check_outlined),
setKey: SettingBoxKey.enableCDN,
ListTile(
title: Text('CDN 设置', style: titleStyle),
leading: Icon(MdiIcons.cloudPlusOutline),
subtitle: Text(
'当前使用:${CDNServiceCode.fromCode(defaultCDNService)!.description},部分 CDN 可能失效,如无法播放请尝试切换',
style: subTitleStyle,
),
onTap: () async {
String? result = await showDialog(
context: context,
builder: (context) {
return SelectDialog<String>(
title: 'CDN 设置',
value: defaultCDNService,
values: CDNService.values.map((e) {
return {'title': e.description, 'value': e.code};
}).toList());
},
);
if (result != null) {
defaultCDNService = result;
setting.put(SettingBoxKey.CDNService, result);
setState(() {});
}
},
),
SetSwitchItem(
title: '音频不跟随 CDN 设置',
subTitle: '直接采用备用 URL可解决部分视频无声',
leading: Icon(MdiIcons.musicNotePlus),
setKey: SettingBoxKey.disableAudioCDN,
defaultVal: true,
),
ListTile(
@@ -117,7 +148,7 @@ class _VideoSettingState extends State<VideoSetting> {
ListTile(
dense: false,
title: Text('默认音质', style: titleStyle),
leading: const Icon(Icons.audiotrack_outlined),
leading: const Icon(Icons.music_video_outlined),
subtitle: Text(
'当前音质:${AudioQualityCode.fromCode(defaultAudioQa)!.description!}',
style: subTitleStyle,

View File

@@ -86,7 +86,7 @@ class VideoDetailController extends GetxController
Floating? floating;
late PreferredSizeWidget headerControl;
late bool enableCDN;
// late bool enableCDN;
late int? cacheVideoQa;
late String cacheDecode;
late String cacheSecondDecode;
@@ -138,7 +138,8 @@ class VideoDetailController extends GetxController
heroTag: heroTag,
);
// CDN优化
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
// enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
// 预设的画质
cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
defaultValue: VideoQuality.values.last.code);
@@ -388,9 +389,10 @@ class VideoDetailController extends GetxController
(e) => e.codecs!.startsWith(currentDecodeFormats.code),
orElse: () => videosList.first);
videoUrl = enableCDN
? VideoUtils.getCdnUrl(firstVideo)
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
// videoUrl = enableCDN
// ? VideoUtils.getCdnUrl(firstVideo)
// : (firstVideo.backupUrl ?? firstVideo.baseUrl!);
videoUrl = VideoUtils.getCdnUrl(firstVideo);
/// 优先顺序 设置中指定质量 -> 当前可选的最高质量
late AudioItem? firstAudio;
@@ -415,9 +417,10 @@ class VideoDetailController extends GetxController
}
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber,
orElse: () => audiosList.first);
audioUrl = enableCDN
? VideoUtils.getCdnUrl(firstAudio)
: (firstAudio.backupUrl ?? firstAudio.baseUrl!);
// audioUrl = enableCDN
// ? VideoUtils.getCdnUrl(firstAudio)
// : (firstAudio.backupUrl ?? firstAudio.baseUrl!);
audioUrl = VideoUtils.getCdnUrl(firstAudio);
if (firstAudio.id != null) {
currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!;
}

View File

@@ -115,7 +115,9 @@ class SettingBoxKey {
allowRotateScreen = 'allowRotateScreen',
horizontalScreen = 'horizontalScreen',
p1080 = 'p1080',
enableCDN = 'enableCDN',
CDNService = 'CDNService',
disableAudioCDN = 'disableAudioCDN',
// enableCDN = 'enableCDN',
autoPiP = 'autoPiP',
pipNoDanmaku = 'pipNoDanmaku',
enableAutoLongPressSpeed = 'enableAutoLongPressSpeed',

View File

@@ -1,44 +1,101 @@
import 'package:PiliPalaX/models/video/play/CDN.dart';
import 'package:PiliPalaX/models/video/play/url.dart';
import 'package:PiliPalaX/utils/storage.dart';
import '../models/live/room_info.dart';
class VideoUtils {
static String getCdnUrl(dynamic item) {
var backupUrl = "";
var videoUrl = "";
static bool isMCDNorPCDN(String url) {
return url.contains("szbdyd.com") ||
url.contains(".mcdn.bilivideo") ||
RegExp(r'^https?://\d{1,3}\.\d{1,3}').hasMatch(url);
}
/// 先获取backupUrl 一般是upgcxcode地址 播放更稳定
if (item is VideoItem) {
backupUrl = item.backupUrl ?? "";
videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
} else if (item is AudioItem) {
backupUrl = item.backupUrl ?? "";
videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
} else if (item is CodecItem) {
static String getCdnUrl(dynamic item) {
String? backupUrl;
String? videoUrl;
String defaultCDNService = GStorage.setting
.get(SettingBoxKey.CDNService, defaultValue: CDNService.ali.code);
if (item is AudioItem) {
if (GStorage.setting
.get(SettingBoxKey.disableAudioCDN, defaultValue: true)) {
return item.backupUrl ?? item.baseUrl ?? "";
}
}
if (defaultCDNService == CDNService.baseUrl.code) {
return item.baseUrl ?? "";
}
if (item is CodecItem) {
backupUrl = (item.urlInfo?.first.host)! +
item.baseUrl! +
item.urlInfo!.first.extra!;
videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
} else {
backupUrl = item.backupUrl;
}
if (defaultCDNService == CDNService.backupUrl.code) {
return backupUrl ?? item.baseUrl ?? "";
}
videoUrl = (backupUrl == null || isMCDNorPCDN(backupUrl))
? item.baseUrl
: backupUrl;
if (videoUrl == null) {
return "";
}
print("videoUrl:$videoUrl");
/// issues #70
if (videoUrl.contains(".mcdn.bilivideo")) {
String defaultCDNHost = CDNServiceCode.fromCode(defaultCDNService)!.host;
print("defaultCDNHost:$defaultCDNHost");
if (videoUrl.contains("szbdyd.com")) {
String hostname =
Uri.parse(videoUrl).queryParameters['xy_usource'] ?? defaultCDNHost;
videoUrl =
'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}';
Uri.parse(videoUrl).replace(host: hostname, port: 443).toString();
} else if (videoUrl.contains(".mcdn.bilivideo")) {
videoUrl = Uri.parse(videoUrl)
.replace(host: defaultCDNHost, port: 443)
.toString();
// videoUrl =
// 'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}';
} else if (videoUrl.contains("/upgcxcode/")) {
//CDN列表
var cdnList = {
'ali': 'upos-sz-mirrorali.bilivideo.com',
'cos': 'upos-sz-mirrorcos.bilivideo.com',
'hw': 'upos-sz-mirrorhw.bilivideo.com',
};
//取一个CDN
var cdn = cdnList['ali'] ?? "";
var reg = RegExp(r'(http|https)://(.*?)/upgcxcode/');
videoUrl = videoUrl.replaceAll(reg, "https://$cdn/upgcxcode/");
videoUrl = Uri.parse(videoUrl)
.replace(host: defaultCDNHost, port: 443)
.toString();
}
print("videoUrl:$videoUrl");
// /// 先获取backupUrl 一般是upgcxcode地址 播放更稳定
// if (item is VideoItem) {
// backupUrl = item.backupUrl ?? "";
// videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
// } else if (item is AudioItem) {
// backupUrl = item.backupUrl ?? "";
// videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
// } else if (item is CodecItem) {
// backupUrl = (item.urlInfo?.first.host)! +
// item.baseUrl! +
// item.urlInfo!.first.extra!;
// videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? "");
// } else {
// return "";
// }
//
// /// issues #70
// if (videoUrl.contains(".mcdn.bilivideo")) {
// videoUrl =
// 'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}';
// } else if (videoUrl.contains("/upgcxcode/")) {
// //CDN列表
// var cdnList = {
// 'ali': 'upos-sz-mirrorali.bilivideo.com',
// 'cos': 'upos-sz-mirrorcos.bilivideo.com',
// 'hw': 'upos-sz-mirrorhw.bilivideo.com',
// };
// //取一个CDN
// var cdn = cdnList['cos'] ?? "";
// var reg = RegExp(r'(http|https)://(.*?)/upgcxcode/');
// videoUrl = videoUrl.replaceAll(reg, "https://$cdn/upgcxcode/");
// }
return videoUrl;
}