mod: CDN优化 issues #70
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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: '进入详情页自动播放',
|
||||||
|
@ -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!)!;
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 暂停播放
|
/// 暂停播放
|
||||||
|
@ -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';
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
# 音量、亮度、屏幕控制
|
# 音量、亮度、屏幕控制
|
||||||
|
Reference in New Issue
Block a user