mod: CDN优化 issues #70
This commit is contained in:
@ -67,6 +67,12 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||
setKey: SettingBoxKey.p1080,
|
||||
defaultVal: true,
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: 'CDN优化',
|
||||
subTitle: '使用优质CDN线路',
|
||||
setKey: SettingBoxKey.enableCDN,
|
||||
defaultVal: true,
|
||||
),
|
||||
const SetSwitchItem(
|
||||
title: '自动播放',
|
||||
subTitle: '进入详情页自动播放',
|
||||
|
@ -16,6 +16,7 @@ import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
||||
import 'package:pilipala/plugin/pl_player/index.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
import 'package:pilipala/utils/video_utils.dart';
|
||||
import 'package:screen_brightness/screen_brightness.dart';
|
||||
|
||||
import 'widgets/header_control.dart';
|
||||
@ -83,6 +84,11 @@ class VideoDetailController extends GetxController
|
||||
Floating? floating;
|
||||
late PreferredSizeWidget headerControl;
|
||||
|
||||
late bool enableCDN;
|
||||
late int? cacheVideoQa;
|
||||
late String cacheDecode;
|
||||
late int cacheAudioQa;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@ -120,6 +126,15 @@ class VideoDetailController extends GetxController
|
||||
videoDetailCtr: this,
|
||||
floating: floating,
|
||||
);
|
||||
// CDN优化
|
||||
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
|
||||
// 预设的画质
|
||||
cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa);
|
||||
// 预设的解码格式
|
||||
cacheDecode = setting.get(SettingBoxKey.defaultDecode,
|
||||
defaultValue: VideoDecodeFormats.values.last.code);
|
||||
cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
||||
defaultValue: AudioQuality.hiRes.code);
|
||||
}
|
||||
|
||||
showReplyReplyPanel() {
|
||||
@ -231,22 +246,19 @@ class VideoDetailController extends GetxController
|
||||
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||
if (result['status']) {
|
||||
data = result['data'];
|
||||
|
||||
List<VideoItem> allVideosList = data.dash!.video!;
|
||||
|
||||
try {
|
||||
// 当前可播放的最高质量视频
|
||||
int currentHighVideoQa = allVideosList.first.quality!.code;
|
||||
// 使用预设的画质 | 当前可用的最高质量
|
||||
int cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
|
||||
defaultValue: currentHighVideoQa);
|
||||
// 预设的画质为null,则当前可用的最高质量
|
||||
cacheVideoQa ??= currentHighVideoQa;
|
||||
int resVideoQa = currentHighVideoQa;
|
||||
if (cacheVideoQa <= currentHighVideoQa) {
|
||||
if (cacheVideoQa! <= currentHighVideoQa) {
|
||||
// 如果预设的画质低于当前最高
|
||||
List<int> numbers = data.acceptQuality!
|
||||
.where((e) => e <= currentHighVideoQa)
|
||||
.toList();
|
||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa, numbers);
|
||||
resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
|
||||
}
|
||||
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
|
||||
|
||||
@ -260,9 +272,7 @@ class VideoDetailController extends GetxController
|
||||
List supportDecodeFormats =
|
||||
supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!;
|
||||
// 默认从设置中取AVC
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get(
|
||||
SettingBoxKey.defaultDecode,
|
||||
defaultValue: VideoDecodeFormats.values.last.code))!;
|
||||
currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
|
||||
try {
|
||||
// 当前视频没有对应格式返回第一个
|
||||
bool flag = false;
|
||||
@ -285,7 +295,9 @@ class VideoDetailController extends GetxController
|
||||
} catch (_) {
|
||||
firstVideo = videosList.first;
|
||||
}
|
||||
videoUrl = firstVideo.baseUrl!;
|
||||
videoUrl = enableCDN
|
||||
? VideoUtils.getCdnUrl(firstVideo)
|
||||
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('firstVideo error: $err');
|
||||
}
|
||||
@ -295,8 +307,6 @@ class VideoDetailController extends GetxController
|
||||
List<AudioItem> audiosList = data.dash!.audio!;
|
||||
|
||||
try {
|
||||
int resultAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
|
||||
defaultValue: AudioQuality.hiRes.code);
|
||||
if (data.dash!.dolby?.audio?.isNotEmpty == true) {
|
||||
// 杜比
|
||||
audiosList.insert(0, data.dash!.dolby!.audio!.first);
|
||||
@ -309,9 +319,9 @@ class VideoDetailController extends GetxController
|
||||
|
||||
if (audiosList.isNotEmpty) {
|
||||
List<int> numbers = audiosList.map((map) => map.id!).toList();
|
||||
int closestNumber = Utils.findClosestNumber(resultAudioQa, numbers);
|
||||
if (!numbers.contains(resultAudioQa) &&
|
||||
numbers.any((e) => e > resultAudioQa)) {
|
||||
int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
|
||||
if (!numbers.contains(cacheAudioQa) &&
|
||||
numbers.any((e) => e > cacheAudioQa)) {
|
||||
closestNumber = 30280;
|
||||
}
|
||||
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber);
|
||||
@ -323,7 +333,9 @@ class VideoDetailController extends GetxController
|
||||
SmartDialog.showToast('firstAudio error: $err');
|
||||
}
|
||||
|
||||
audioUrl = firstAudio.baseUrl ?? '';
|
||||
audioUrl = enableCDN
|
||||
? VideoUtils.getCdnUrl(firstAudio)
|
||||
: (firstAudio.backupUrl ?? firstAudio.baseUrl!);
|
||||
//
|
||||
if (firstAudio.id != null) {
|
||||
currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!;
|
||||
|
@ -3,18 +3,15 @@ import 'dart:io';
|
||||
|
||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||
import 'package:floating/floating.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/common/widgets/sliver_header.dart';
|
||||
import 'package:pilipala/http/user.dart';
|
||||
import 'package:pilipala/models/common/search_type.dart';
|
||||
import 'package:pilipala/pages/bangumi/introduction/index.dart';
|
||||
import 'package:pilipala/pages/danmaku/view.dart';
|
||||
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
|
||||
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/controller.dart';
|
||||
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
||||
@ -23,7 +20,6 @@ import 'package:pilipala/plugin/pl_player/index.dart';
|
||||
import 'package:pilipala/plugin/pl_player/models/play_repeat.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
import 'widgets/app_bar.dart';
|
||||
import 'widgets/header_control.dart';
|
||||
|
||||
class VideoDetailPage extends StatefulWidget {
|
||||
|
@ -332,12 +332,11 @@ class PlPlayerController {
|
||||
// 数据加载完成
|
||||
dataStatus.status.value = DataStatus.loaded;
|
||||
|
||||
await _initializePlayer(seekTo: seekTo);
|
||||
|
||||
// listen the video player events
|
||||
if (!_listenersInitialized) {
|
||||
startListeners();
|
||||
}
|
||||
await _initializePlayer(seekTo: seekTo);
|
||||
bool autoEnterFullcreen =
|
||||
setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false);
|
||||
if (autoEnterFullcreen && _isFirstTime) {
|
||||
@ -407,6 +406,7 @@ class PlPlayerController {
|
||||
player,
|
||||
configuration: VideoControllerConfiguration(
|
||||
enableHardwareAcceleration: enableHA,
|
||||
androidAttachSurfaceAfterVideoParameters: false,
|
||||
),
|
||||
);
|
||||
|
||||
@ -542,6 +542,7 @@ class PlPlayerController {
|
||||
}
|
||||
_position.value = position;
|
||||
_heartDuration = position.inSeconds;
|
||||
print('seek duration: $duration');
|
||||
if (duration.value.inSeconds != 0) {
|
||||
if (type != 'slider') {
|
||||
/// 拖动进度条调节时,不等待第一帧,防止抖动
|
||||
@ -552,17 +553,19 @@ class PlPlayerController {
|
||||
// play();
|
||||
// }
|
||||
} else {
|
||||
print('seek duration else');
|
||||
_timerForSeek?.cancel();
|
||||
_timerForSeek =
|
||||
Timer.periodic(const Duration(milliseconds: 200), (Timer t) async {
|
||||
//_timerForSeek = null;
|
||||
if (duration.value.inSeconds != 0) {
|
||||
await _videoPlayerController!.stream.buffer.first;
|
||||
await _videoPlayerController?.seek(position);
|
||||
// if (playerStatus.stopped) {
|
||||
// play();
|
||||
// }
|
||||
if (playerStatus.status.value == PlayerStatus.paused) {
|
||||
play();
|
||||
}
|
||||
t.cancel();
|
||||
//_timerForSeek = null;
|
||||
_timerForSeek = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -595,6 +598,8 @@ class PlPlayerController {
|
||||
|
||||
/// 播放视频
|
||||
Future<void> play({bool repeat = false, bool hideControls = true}) async {
|
||||
// 播放时自动隐藏控制条
|
||||
controls = !hideControls;
|
||||
// repeat为true,将从头播放
|
||||
if (repeat) {
|
||||
await seekTo(Duration.zero);
|
||||
@ -606,11 +611,6 @@ class PlPlayerController {
|
||||
|
||||
playerStatus.status.value = PlayerStatus.playing;
|
||||
// screenManager.setOverlays(false);
|
||||
|
||||
// 播放时自动隐藏控制条
|
||||
if (hideControls) {
|
||||
_hideTaskControls();
|
||||
}
|
||||
}
|
||||
|
||||
/// 暂停播放
|
||||
|
@ -105,6 +105,7 @@ class SettingBoxKey {
|
||||
static const String enableAutoEnter = 'enableAutoEnter';
|
||||
static const String enableAutoExit = 'enableAutoExit';
|
||||
static const String p1080 = 'p1080';
|
||||
static const String enableCDN = 'enableCDN';
|
||||
|
||||
// youtube 双击快进快退
|
||||
static const String enableQuickDouble = 'enableQuickDouble';
|
||||
|
36
lib/utils/video_utils.dart
Normal file
36
lib/utils/video_utils.dart
Normal file
@ -0,0 +1,36 @@
|
||||
import 'package:pilipala/models/video/play/url.dart';
|
||||
|
||||
class VideoUtils {
|
||||
static String getCdnUrl(dynamic item) {
|
||||
var backupUrl = "";
|
||||
var 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 {
|
||||
return "";
|
||||
}
|
||||
|
||||
/// issues #70
|
||||
if (videoUrl.contains(".mcdn.bilivideo") ||
|
||||
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/");
|
||||
}
|
||||
|
||||
return videoUrl;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user