mod: CDN优化 issues #70

This commit is contained in:
guozhigq
2023-10-21 01:11:38 +08:00
parent 41e9cfcbbb
commit 9744ec88a0
29 changed files with 89 additions and 38 deletions

View File

@ -67,6 +67,12 @@ class _PlaySettingState extends State<PlaySetting> {
setKey: SettingBoxKey.p1080, setKey: SettingBoxKey.p1080,
defaultVal: true, defaultVal: true,
), ),
const SetSwitchItem(
title: 'CDN优化',
subTitle: '使用优质CDN线路',
setKey: SettingBoxKey.enableCDN,
defaultVal: true,
),
const SetSwitchItem( const SetSwitchItem(
title: '自动播放', title: '自动播放',
subTitle: '进入详情页自动播放', subTitle: '进入详情页自动播放',

View File

@ -16,6 +16,7 @@ import 'package:pilipala/pages/video/detail/replyReply/index.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/utils/video_utils.dart';
import 'package:screen_brightness/screen_brightness.dart'; import 'package:screen_brightness/screen_brightness.dart';
import 'widgets/header_control.dart'; import 'widgets/header_control.dart';
@ -83,6 +84,11 @@ class VideoDetailController extends GetxController
Floating? floating; Floating? floating;
late PreferredSizeWidget headerControl; late PreferredSizeWidget headerControl;
late bool enableCDN;
late int? cacheVideoQa;
late String cacheDecode;
late int cacheAudioQa;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -120,6 +126,15 @@ class VideoDetailController extends GetxController
videoDetailCtr: this, videoDetailCtr: this,
floating: floating, 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() { showReplyReplyPanel() {
@ -231,22 +246,19 @@ class VideoDetailController extends GetxController
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
if (result['status']) { if (result['status']) {
data = result['data']; data = result['data'];
List<VideoItem> allVideosList = data.dash!.video!; List<VideoItem> allVideosList = data.dash!.video!;
try { try {
// 当前可播放的最高质量视频 // 当前可播放的最高质量视频
int currentHighVideoQa = allVideosList.first.quality!.code; int currentHighVideoQa = allVideosList.first.quality!.code;
// 使用预设的画质 当前可用的最高质量 // 预设的画质为null当前可用的最高质量
int cacheVideoQa = setting.get(SettingBoxKey.defaultVideoQa, cacheVideoQa ??= currentHighVideoQa;
defaultValue: currentHighVideoQa);
int resVideoQa = currentHighVideoQa; int resVideoQa = currentHighVideoQa;
if (cacheVideoQa <= currentHighVideoQa) { if (cacheVideoQa! <= currentHighVideoQa) {
// 如果预设的画质低于当前最高 // 如果预设的画质低于当前最高
List<int> numbers = data.acceptQuality! List<int> numbers = data.acceptQuality!
.where((e) => e <= currentHighVideoQa) .where((e) => e <= currentHighVideoQa)
.toList(); .toList();
resVideoQa = Utils.findClosestNumber(cacheVideoQa, numbers); resVideoQa = Utils.findClosestNumber(cacheVideoQa!, numbers);
} }
currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!; currentVideoQa = VideoQualityCode.fromCode(resVideoQa)!;
@ -260,9 +272,7 @@ class VideoDetailController extends GetxController
List supportDecodeFormats = List supportDecodeFormats =
supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!; supportFormats.firstWhere((e) => e.quality == resVideoQa).codecs!;
// 默认从设置中取AVC // 默认从设置中取AVC
currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( currentDecodeFormats = VideoDecodeFormatsCode.fromString(cacheDecode)!;
SettingBoxKey.defaultDecode,
defaultValue: VideoDecodeFormats.values.last.code))!;
try { try {
// 当前视频没有对应格式返回第一个 // 当前视频没有对应格式返回第一个
bool flag = false; bool flag = false;
@ -285,7 +295,9 @@ class VideoDetailController extends GetxController
} catch (_) { } catch (_) {
firstVideo = videosList.first; firstVideo = videosList.first;
} }
videoUrl = firstVideo.baseUrl!; videoUrl = enableCDN
? VideoUtils.getCdnUrl(firstVideo)
: (firstVideo.backupUrl ?? firstVideo.baseUrl!);
} catch (err) { } catch (err) {
SmartDialog.showToast('firstVideo error: $err'); SmartDialog.showToast('firstVideo error: $err');
} }
@ -295,8 +307,6 @@ class VideoDetailController extends GetxController
List<AudioItem> audiosList = data.dash!.audio!; List<AudioItem> audiosList = data.dash!.audio!;
try { try {
int resultAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
defaultValue: AudioQuality.hiRes.code);
if (data.dash!.dolby?.audio?.isNotEmpty == true) { if (data.dash!.dolby?.audio?.isNotEmpty == true) {
// 杜比 // 杜比
audiosList.insert(0, data.dash!.dolby!.audio!.first); audiosList.insert(0, data.dash!.dolby!.audio!.first);
@ -309,9 +319,9 @@ class VideoDetailController extends GetxController
if (audiosList.isNotEmpty) { if (audiosList.isNotEmpty) {
List<int> numbers = audiosList.map((map) => map.id!).toList(); List<int> numbers = audiosList.map((map) => map.id!).toList();
int closestNumber = Utils.findClosestNumber(resultAudioQa, numbers); int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers);
if (!numbers.contains(resultAudioQa) && if (!numbers.contains(cacheAudioQa) &&
numbers.any((e) => e > resultAudioQa)) { numbers.any((e) => e > cacheAudioQa)) {
closestNumber = 30280; closestNumber = 30280;
} }
firstAudio = audiosList.firstWhere((e) => e.id == closestNumber); firstAudio = audiosList.firstWhere((e) => e.id == closestNumber);
@ -323,7 +333,9 @@ class VideoDetailController extends GetxController
SmartDialog.showToast('firstAudio error: $err'); SmartDialog.showToast('firstAudio error: $err');
} }
audioUrl = firstAudio.baseUrl ?? ''; audioUrl = enableCDN
? VideoUtils.getCdnUrl(firstAudio)
: (firstAudio.backupUrl ?? firstAudio.baseUrl!);
// //
if (firstAudio.id != null) { if (firstAudio.id != null) {
currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!; currentAudioQa = AudioQualityCode.fromCode(firstAudio.id!)!;

View File

@ -3,18 +3,15 @@ import 'dart:io';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:floating/floating.dart'; import 'package:floating/floating.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/network_img_layer.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/http/user.dart';
import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/pages/bangumi/introduction/index.dart'; import 'package:pilipala/pages/bangumi/introduction/index.dart';
import 'package:pilipala/pages/danmaku/view.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/reply/index.dart';
import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/controller.dart';
import 'package:pilipala/pages/video/detail/introduction/index.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/plugin/pl_player/models/play_repeat.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'widgets/app_bar.dart';
import 'widgets/header_control.dart'; import 'widgets/header_control.dart';
class VideoDetailPage extends StatefulWidget { class VideoDetailPage extends StatefulWidget {

View File

@ -332,12 +332,11 @@ class PlPlayerController {
// 数据加载完成 // 数据加载完成
dataStatus.status.value = DataStatus.loaded; dataStatus.status.value = DataStatus.loaded;
await _initializePlayer(seekTo: seekTo);
// listen the video player events // listen the video player events
if (!_listenersInitialized) { if (!_listenersInitialized) {
startListeners(); startListeners();
} }
await _initializePlayer(seekTo: seekTo);
bool autoEnterFullcreen = bool autoEnterFullcreen =
setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false); setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false);
if (autoEnterFullcreen && _isFirstTime) { if (autoEnterFullcreen && _isFirstTime) {
@ -407,6 +406,7 @@ class PlPlayerController {
player, player,
configuration: VideoControllerConfiguration( configuration: VideoControllerConfiguration(
enableHardwareAcceleration: enableHA, enableHardwareAcceleration: enableHA,
androidAttachSurfaceAfterVideoParameters: false,
), ),
); );
@ -542,6 +542,7 @@ class PlPlayerController {
} }
_position.value = position; _position.value = position;
_heartDuration = position.inSeconds; _heartDuration = position.inSeconds;
print('seek duration: $duration');
if (duration.value.inSeconds != 0) { if (duration.value.inSeconds != 0) {
if (type != 'slider') { if (type != 'slider') {
/// 拖动进度条调节时,不等待第一帧,防止抖动 /// 拖动进度条调节时,不等待第一帧,防止抖动
@ -552,17 +553,19 @@ class PlPlayerController {
// play(); // play();
// } // }
} else { } else {
print('seek duration else');
_timerForSeek?.cancel(); _timerForSeek?.cancel();
_timerForSeek = _timerForSeek =
Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { Timer.periodic(const Duration(milliseconds: 200), (Timer t) async {
//_timerForSeek = null; //_timerForSeek = null;
if (duration.value.inSeconds != 0) { if (duration.value.inSeconds != 0) {
await _videoPlayerController!.stream.buffer.first;
await _videoPlayerController?.seek(position); await _videoPlayerController?.seek(position);
// if (playerStatus.stopped) { if (playerStatus.status.value == PlayerStatus.paused) {
// play(); play();
// } }
t.cancel(); t.cancel();
//_timerForSeek = null; _timerForSeek = null;
} }
}); });
} }
@ -595,6 +598,8 @@ class PlPlayerController {
/// 播放视频 /// 播放视频
Future<void> play({bool repeat = false, bool hideControls = true}) async { Future<void> play({bool repeat = false, bool hideControls = true}) async {
// 播放时自动隐藏控制条
controls = !hideControls;
// repeat为true将从头播放 // repeat为true将从头播放
if (repeat) { if (repeat) {
await seekTo(Duration.zero); await seekTo(Duration.zero);
@ -606,11 +611,6 @@ class PlPlayerController {
playerStatus.status.value = PlayerStatus.playing; playerStatus.status.value = PlayerStatus.playing;
// screenManager.setOverlays(false); // screenManager.setOverlays(false);
// 播放时自动隐藏控制条
if (hideControls) {
_hideTaskControls();
}
} }
/// 暂停播放 /// 暂停播放

View File

@ -105,6 +105,7 @@ class SettingBoxKey {
static const String enableAutoEnter = 'enableAutoEnter'; static const String enableAutoEnter = 'enableAutoEnter';
static const String enableAutoExit = 'enableAutoExit'; static const String enableAutoExit = 'enableAutoExit';
static const String p1080 = 'p1080'; static const String p1080 = 'p1080';
static const String enableCDN = 'enableCDN';
// youtube 双击快进快退 // youtube 双击快进快退
static const String enableQuickDouble = 'enableQuickDouble'; static const String enableQuickDouble = 'enableQuickDouble';

View 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;
}
}

View File

@ -761,10 +761,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit name: media_kit
sha256: d652c2bdb0cd876bf1046e24d0b614651fefe59f7c3a2d9b7ed57217b9e7db94 sha256: "3dffc6d0c19117d51fbc42a7f89612e0595665800a596289ab7a80bdd93e0ad1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.8+1" version: "1.1.9"
media_kit_libs_android_video: media_kit_libs_android_video:
dependency: transitive dependency: transitive
description: description:
@ -825,10 +825,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: media_kit_video name: media_kit_video
sha256: b1a427f0540c5f052dfab73e4b76a5eb8efa7ebb5d83179cb23fc3932afc315a sha256: b8df9cf97aba1861af83b00ac16f5cac536debe0781a934a554b77c157a8f7e8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.2.2"
meta: meta:
dependency: transitive dependency: transitive
description: description:

View File

@ -84,8 +84,8 @@ dependencies:
crypto: ^3.0.3 crypto: ^3.0.3
# 视频播放器 # 视频播放器
media_kit: ^1.1.8 # Primary package. media_kit: ^1.1.9 # Primary package.
media_kit_video: ^1.2.1 # For video rendering. media_kit_video: ^1.2.2 # For video rendering.
media_kit_libs_video: ^1.0.3 media_kit_libs_video: ^1.0.3
# 音量、亮度、屏幕控制 # 音量、亮度、屏幕控制