231 lines
6.6 KiB
Dart
231 lines
6.6 KiB
Dart
import 'dart:convert';
|
|
import 'dart:ui';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:hive/hive.dart';
|
|
import 'package:ns_danmaku/ns_danmaku.dart';
|
|
import 'package:pilipala/http/constants.dart';
|
|
import 'package:pilipala/http/init.dart';
|
|
import 'package:pilipala/http/live.dart';
|
|
import 'package:pilipala/models/live/message.dart';
|
|
import 'package:pilipala/models/live/quality.dart';
|
|
import 'package:pilipala/models/live/room_info.dart';
|
|
import 'package:pilipala/plugin/pl_player/index.dart';
|
|
import 'package:pilipala/plugin/pl_socket/index.dart';
|
|
import 'package:pilipala/utils/live.dart';
|
|
import '../../models/live/room_info_h5.dart';
|
|
import '../../utils/storage.dart';
|
|
import '../../utils/video_utils.dart';
|
|
|
|
class LiveRoomController extends GetxController {
|
|
String cover = '';
|
|
late int roomId;
|
|
dynamic liveItem;
|
|
late String heroTag;
|
|
double volume = 0.0;
|
|
// 静音状态
|
|
RxBool volumeOff = false.obs;
|
|
PlPlayerController plPlayerController = PlPlayerController(videoType: 'live');
|
|
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
|
|
late bool enableCDN;
|
|
late int currentQn;
|
|
int? tempCurrentQn;
|
|
late List<Map<String, dynamic>> acceptQnList;
|
|
RxString currentQnDesc = ''.obs;
|
|
Box userInfoCache = GStrorage.userInfo;
|
|
int userId = 0;
|
|
PlSocket? plSocket;
|
|
List<String> danmuHostList = [];
|
|
String token = '';
|
|
// 弹幕消息列表
|
|
RxList<LiveMessageModel> messageList = <LiveMessageModel>[].obs;
|
|
DanmakuController? danmakuController;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
currentQn = setting.get(SettingBoxKey.defaultLiveQa,
|
|
defaultValue: LiveQuality.values.last.code);
|
|
roomId = int.parse(Get.parameters['roomid']!);
|
|
if (Get.arguments != null) {
|
|
liveItem = Get.arguments['liveItem'];
|
|
heroTag = Get.arguments['heroTag'] ?? '';
|
|
if (liveItem != null && liveItem.pic != null && liveItem.pic != '') {
|
|
cover = liveItem.pic;
|
|
}
|
|
if (liveItem != null && liveItem.cover != null && liveItem.cover != '') {
|
|
cover = liveItem.cover;
|
|
}
|
|
}
|
|
// CDN优化
|
|
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
|
|
final userInfo = userInfoCache.get('userInfoCache');
|
|
if (userInfo != null && userInfo.mid != null) {
|
|
userId = userInfo.mid;
|
|
}
|
|
liveDanmakuInfo().then((value) => initSocket());
|
|
}
|
|
|
|
playerInit(source) async {
|
|
await plPlayerController.setDataSource(
|
|
DataSource(
|
|
videoSource: source,
|
|
audioSource: null,
|
|
type: DataSourceType.network,
|
|
httpHeaders: {
|
|
'user-agent':
|
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15',
|
|
'referer': HttpString.baseUrl
|
|
},
|
|
),
|
|
// 硬解
|
|
enableHA: true,
|
|
autoplay: true,
|
|
);
|
|
}
|
|
|
|
Future queryLiveInfo() async {
|
|
var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn);
|
|
if (res['status']) {
|
|
List<CodecItem> codec =
|
|
res['data'].playurlInfo.playurl.stream.first.format.first.codec;
|
|
CodecItem item = codec.first;
|
|
// 以服务端返回的码率为准
|
|
currentQn = item.currentQn!;
|
|
if (tempCurrentQn != null && tempCurrentQn == currentQn) {
|
|
SmartDialog.showToast('画质切换失败,请检查登录状态');
|
|
}
|
|
List acceptQn = item.acceptQn!;
|
|
acceptQnList = acceptQn.map((e) {
|
|
return {
|
|
'code': e,
|
|
'desc': LiveQuality.values
|
|
.firstWhere((element) => element.code == e)
|
|
.description,
|
|
};
|
|
}).toList();
|
|
currentQnDesc.value = LiveQuality.values
|
|
.firstWhere((element) => element.code == currentQn)
|
|
.description;
|
|
String videoUrl = enableCDN
|
|
? VideoUtils.getCdnUrl(item)
|
|
: (item.urlInfo?.first.host)! +
|
|
item.baseUrl! +
|
|
item.urlInfo!.first.extra!;
|
|
await playerInit(videoUrl);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
void setVolumn(value) {
|
|
if (value == 0) {
|
|
// 设置音量
|
|
volumeOff.value = false;
|
|
} else {
|
|
// 取消音量
|
|
volume = value;
|
|
volumeOff.value = true;
|
|
}
|
|
}
|
|
|
|
Future queryLiveInfoH5() async {
|
|
var res = await LiveHttp.liveRoomInfoH5(roomId: roomId);
|
|
if (res['status']) {
|
|
roomInfoH5.value = res['data'];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// 修改画质
|
|
void changeQn(int qn) async {
|
|
tempCurrentQn = currentQn;
|
|
if (currentQn == qn) {
|
|
return;
|
|
}
|
|
currentQn = qn;
|
|
currentQnDesc.value = LiveQuality.values
|
|
.firstWhere((element) => element.code == currentQn)
|
|
.description;
|
|
await queryLiveInfo();
|
|
}
|
|
|
|
Future liveDanmakuInfo() async {
|
|
var res = await LiveHttp.liveDanmakuInfo(roomId: roomId);
|
|
if (res['status']) {
|
|
danmuHostList = (res["data"]["host_list"] as List)
|
|
.map<String>((e) => '${e["host"]}:${e['wss_port']}')
|
|
.toList();
|
|
token = res["data"]["token"];
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// 建立socket
|
|
void initSocket() async {
|
|
final wsUrl = danmuHostList.isNotEmpty
|
|
? danmuHostList.first
|
|
: "broadcastlv.chat.bilibili.com";
|
|
plSocket = PlSocket(
|
|
url: 'wss://$wsUrl/sub',
|
|
heartTime: 30,
|
|
onReadyCb: () {
|
|
joinRoom();
|
|
},
|
|
onMessageCb: (message) {
|
|
final List<LiveMessageModel>? liveMsg =
|
|
LiveUtils.decodeMessage(message);
|
|
if (liveMsg != null) {
|
|
// 过滤出聊天消息
|
|
var chatMessages =
|
|
liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList();
|
|
|
|
// 添加到 messageList
|
|
messageList.addAll(chatMessages);
|
|
|
|
// 将 chatMessages 转换为 danmakuItems 列表
|
|
List<DanmakuItem> danmakuItems = chatMessages.map<DanmakuItem>((e) {
|
|
return DanmakuItem(
|
|
e.message ?? '',
|
|
color: Color.fromARGB(
|
|
255,
|
|
e.color.r,
|
|
e.color.g,
|
|
e.color.b,
|
|
),
|
|
);
|
|
}).toList();
|
|
|
|
// 添加到 danmakuController
|
|
danmakuController?.addItems(danmakuItems);
|
|
}
|
|
},
|
|
onErrorCb: (e) {
|
|
print('error: $e');
|
|
},
|
|
);
|
|
await plSocket?.connect();
|
|
}
|
|
|
|
void joinRoom() async {
|
|
var joinData = LiveUtils.encodeData(
|
|
json.encode({
|
|
"uid": userId,
|
|
"roomid": roomId,
|
|
"protover": 3,
|
|
"buvid": Request.buvid,
|
|
"platform": "web",
|
|
"type": 2,
|
|
"key": token,
|
|
}),
|
|
7,
|
|
);
|
|
plSocket?.sendMessage(joinData);
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
plSocket?.onClose();
|
|
super.onClose();
|
|
}
|
|
}
|