replace grpc to dio (#313)

* replace grpc to dio

* load danmaku from grpc
This commit is contained in:
My-Responsitories
2025-02-24 06:55:28 +00:00
committed by GitHub
parent 9e242fb902
commit 347a704b54
14 changed files with 247 additions and 2327 deletions

View File

@@ -1,26 +1,48 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/grpc/app/dynamic/v1/dynamic.pb.dart';
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pb.dart';
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
import 'package:PiliPlus/grpc/app/playeronline/v1/playeronline.pbgrpc.dart';
import 'package:PiliPlus/grpc/app/playeronline/v1/playeronline.pb.dart';
import 'package:PiliPlus/grpc/app/show/popular/v1/popular.pb.dart';
import 'package:PiliPlus/grpc/device/device.pb.dart';
import 'package:PiliPlus/grpc/dm/v1/dm.pb.dart';
import 'package:PiliPlus/grpc/fawkes/fawkes.pb.dart';
import 'package:PiliPlus/grpc/grpc_client.dart';
import 'package:PiliPlus/grpc/locale/locale.pb.dart';
import 'package:PiliPlus/grpc/metadata/metadata.pb.dart';
import 'package:PiliPlus/grpc/network/network.pb.dart' as network;
import 'package:PiliPlus/grpc/restriction/restriction.pb.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/utils/login.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:archive/archive.dart';
import 'package:dio/dio.dart';
import 'package:fixnum/src/int64.dart';
import 'package:flutter/material.dart';
import 'package:grpc/grpc.dart';
import 'package:protobuf/protobuf.dart' show GeneratedMessage;
class GrpcUrl {
static const playerOnline =
'/bilibili.app.playeronline.v1.PlayerOnline/PlayerOnline';
static const popular = '/bilibili.app.show.v1.Popular/Index';
static const dialogList =
'/bilibili.main.community.reply.v1.Reply/DialogList';
static const detailList =
'/bilibili.main.community.reply.v1.Reply/DetailList';
static const replyInfo = '/bilibili.main.community.reply.v1.Reply/ReplyInfo';
static const mainList = '/bilibili.main.community.reply.v1.Reply/MainList';
static const dynSpace = '/bilibili.app.dynamic.v2.Dynamic/DynSpace';
static const dynRed = '/bilibili.app.dynamic.v1.Dynamic/DynRed';
static const dmSegMobile = '/bilibili.community.service.dm.v1.DM/DmSegMobile';
}
class GrpcRepo {
static const gzipEncoder = GZipEncoder();
static const gzipDecoder = GZipDecoder();
static final bool _isLogin = GStorage.userInfo.get('userInfoCache') != null;
static final int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
static final String? _accessKey = GStorage.localCache
@@ -35,7 +57,10 @@ class GrpcRepo {
static final _traceId = Utils.genTraceId();
static final _sessionId = Utils.generateRandomString(8);
static final Map<String, String> metadata = {
static final Map<String, String> headers = {
Headers.contentTypeHeader: 'application/grpc',
'grpc-encoding': 'gzip',
'gzip-accept-encoding': 'gzip,identity',
'user-agent': '${Constants.userAgent} grpc-java-cronet/1.36.1',
'x-bili-gaia-vtoken': '',
'x-bili-aurora-eid': _isLogin ? _eId : '',
@@ -46,108 +71,132 @@ class GrpcRepo {
'buvid': _buvid,
'bili-http-engine': 'cronet',
'te': 'trailers',
'x-bili-fawkes-req-bin': base64Encode((FawkesReq()
..appkey = _mobiApp
..env = 'prod'
..sessionId = _sessionId)
'x-bili-fawkes-req-bin': base64Encode(
FawkesReq(appkey: _mobiApp, env: 'prod', sessionId: _sessionId)
.writeToBuffer()),
'x-bili-metadata-bin': base64Encode(Metadata(
accessKey: _accessKey ?? '',
mobiApp: _mobiApp,
device: _phone,
build: _build,
channel: _biliChannel,
buvid: _buvid,
platform: _mobiApp,
).writeToBuffer()),
'x-bili-device-bin': base64Encode(Device(
appId: 1,
build: _build,
buvid: _buvid,
mobiApp: _mobiApp,
platform: _mobiApp,
device: _phone,
channel: _biliChannel,
brand: _phone,
model: _phone,
osver: '14',
fpLocal: '',
fpRemote: '',
versionName: _build.toString(),
fp: '',
fts: Int64())
.writeToBuffer()),
'x-bili-metadata-bin': base64Encode((Metadata()
..accessKey = _accessKey ?? ''
..mobiApp = _mobiApp
..device = _phone
..build = _build
..channel = _biliChannel
..buvid = _buvid
..platform = _mobiApp)
'x-bili-network-bin': base64Encode(network.Network(
type: network.NetworkType.WIFI,
tf: network.TFType.TF_UNKNOWN,
oid: '')
.writeToBuffer()),
'x-bili-device-bin': base64Encode((Device()
..appId = 1
..build = _build
..buvid = _buvid
..mobiApp = _mobiApp
..platform = _mobiApp
..device = _phone
..channel = _biliChannel
..brand = _phone
..model = _phone
..osver = '14'
..fpLocal = ''
..fpRemote = ''
..versionName = _build.toString()
..fp = ''
..fts = Int64())
'x-bili-restriction-bin': base64Encode(Restriction(
teenagersMode: false,
lessonsMode: false,
mode: ModeType.NORMAL,
review: false,
disableRcmd: false,
basicMode: false)
.writeToBuffer()),
'x-bili-network-bin': base64Encode((network.Network()
..type = network.NetworkType.WIFI
..tf = network.TFType.TF_UNKNOWN
..oid = '')
.writeToBuffer()),
'x-bili-restriction-bin': base64Encode((Restriction()
..teenagersMode = false
..lessonsMode = false
..mode = ModeType.NORMAL
..review = false
..disableRcmd = false
..basicMode = false)
.writeToBuffer()),
'x-bili-locale-bin': base64Encode((Locale()
..cLocale = LocaleIds(language: 'zh', region: 'CN')
..sLocale = LocaleIds(language: 'zh', region: 'CN')
..simCode = ''
..timezone = 'Asia/Shanghai')
'x-bili-locale-bin': base64Encode(Locale(
cLocale: LocaleIds(language: 'zh', region: 'CN'),
sLocale: LocaleIds(language: 'zh', region: 'CN'),
simCode: '',
timezone: 'Asia/Shanghai')
.writeToBuffer()),
'x-bili-exps-bin': '',
};
static final CallOptions options = CallOptions(metadata: metadata);
static final unprintableRegExp =
RegExp(r"[^\0u0020-\u007e\u4e00-\u9fa5]");
static Future _request(Function request) async {
try {
return await request();
} catch (e) {
dynamic defMsg() => {'status': false, 'msg': e.toString()};
if (e is GrpcError) {
try {
String msg = utf8.decode(
e.details?.firstOrNull?.getFieldOrNull(2),
allowMalformed: true,
);
msg =
msg.replaceAll(RegExp(r"[^a-zA-Z0-9\u4e00-\u9fa5,.;?,。;!?]"), '');
if (msg.isNotEmpty) {
return {'status': false, 'msg': msg};
} else {
return defMsg();
}
} catch (e1) {
debugPrint(e1.toString());
return defMsg();
}
}
return defMsg();
static Uint8List compressProtobuf(Uint8List proto) {
proto = gzipEncoder.encodeBytes(proto, level: 0);
var byteLength = ByteData(4);
byteLength.setInt32(0, proto.length, Endian.big);
var compressed = Uint8List(5 + proto.length);
compressed[0] = 1;
compressed.setRange(1, 5, byteLength.buffer.asUint8List());
compressed.setAll(5, proto);
return compressed;
}
static Uint8List decompressProtobuf(Uint8List data) {
var length = ByteData.sublistView(data, 1, 5).getInt32(0, Endian.big);
if (data[0] == 1) {
return gzipDecoder.decodeBytes(data.sublist(5, length + 5));
} else {
return data.sublist(5, length + 5);
}
}
static Future<Map<String, dynamic>> _request(
url, GeneratedMessage request, Function grpcParser,
{Function? onSuccess}) async {
final response = await Request().post(HttpString.appBaseUrl + url,
data: compressProtobuf(request.writeToBuffer()),
options: Options(headers: headers, responseType: ResponseType.bytes));
if (response.data is Map) {
return {'status': false, 'msg': response.data['message']};
}
Uint8List data = response.data;
// if (int.tryParse(response.headers.value('grpc-status') ?? '0') == 0) {
// try {
data = decompressProtobuf(data);
final grpcResponse = grpcParser(data);
return {
'status': true,
'data': onSuccess == null ? grpcResponse : onSuccess(grpcResponse)
};
// } catch (e) {}
// }
// if (data.isNotEmpty) {
// try {
// String msg = utf8
// .decode(data, allowMalformed: true)
// .replaceAll(unprintableRegExp, '');
// if (msg.isNotEmpty) {
// return {'status': false, 'msg': msg};
// }
// } catch (e1) {}
// }
// return {'status': false, 'msg': response.headers.map.toString()};
}
static Future playerOnline({
int aid = 0,
int cid = 0,
}) async {
return await _request(() async {
final request = PlayerOnlineReq()
..aid = Int64(aid)
..cid = Int64(cid)
..playOpen = true;
final response = await GrpcClient.instance.playerOnlineClient
.playerOnline(request, options: options);
return {'status': true, 'data': response.totalNumberText};
});
return await _request(
GrpcUrl.playerOnline,
PlayerOnlineReq(aid: Int64(aid), cid: Int64(cid), playOpen: true),
PlayerOnlineReply.fromBuffer,
onSuccess: (response) => response.totalNumberText);
}
static Future popular(int idx) async {
return await _request(() async {
final request = PopularResultReq()..idx = Int64(idx);
final response = await GrpcClient.instance.popularClient
.index(request, options: options);
return await _request(GrpcUrl.popular, PopularResultReq(idx: Int64(idx)),
PopularReply.fromBuffer, onSuccess: (response) {
response.items.retainWhere((item) => item.smallCoverV5.base.goto == 'av');
return {'status': true, 'data': response.items};
});
@@ -161,17 +210,15 @@ class GrpcRepo {
required CursorReq cursor,
DetailListScene scene = DetailListScene.REPLY,
}) async {
return await _request(() async {
final request = DialogListReq()
..oid = Int64(oid)
..type = Int64(type)
..root = Int64(root)
..rpid = Int64(rpid)
..cursor = cursor;
final response = await GrpcClient.instance.replyClient
.dialogList(request, options: options);
return {'status': true, 'data': response};
});
return await _request(
GrpcUrl.dialogList,
DialogListReq(
oid: Int64(oid),
type: Int64(type),
root: Int64(root),
rpid: Int64(rpid),
cursor: cursor),
DialogListReply.fromBuffer);
}
static Future detailList({
@@ -182,29 +229,22 @@ class GrpcRepo {
required CursorReq cursor,
DetailListScene scene = DetailListScene.REPLY,
}) async {
return await _request(() async {
final request = DetailListReq()
..oid = Int64(oid)
..type = Int64(type)
..root = Int64(root)
..rpid = Int64(rpid)
..cursor = cursor
..scene = scene;
final response = await GrpcClient.instance.replyClient
.detailList(request, options: options);
return {'status': true, 'data': response};
});
return await _request(
GrpcUrl.detailList,
DetailListReq(
oid: Int64(oid),
type: Int64(type),
root: Int64(root),
rpid: Int64(rpid),
cursor: cursor,
scene: scene),
DetailListReply.fromBuffer);
}
static Future replyInfo({
required int rpid,
}) async {
return await _request(() async {
final request = ReplyInfoReq()..rpid = Int64(rpid);
final response = await GrpcClient.instance.replyClient
.replyInfo(request, options: options);
return {'status': true, 'data': response.reply};
});
static Future replyInfo({required int rpid}) async {
return await _request(GrpcUrl.replyInfo, ReplyInfoReq(rpid: Int64(rpid)),
ReplyInfoReply.fromBuffer,
onSuccess: (response) => response.reply);
}
static Future mainList({
@@ -212,40 +252,39 @@ class GrpcRepo {
required int oid,
required CursorReq cursor,
}) async {
return await _request(() async {
final request = MainListReq()
..oid = Int64(oid)
..type = Int64(type)
..rpid = Int64(0)
..cursor = cursor;
final response = await GrpcClient.instance.replyClient
.mainList(request, options: options);
return {'status': true, 'data': response};
});
return await _request(
GrpcUrl.mainList,
MainListReq(
oid: Int64(oid), type: Int64(type), rpid: Int64(0), cursor: cursor),
MainListReply.fromBuffer);
}
static Future dynSpace({
required int uid,
required int page,
}) async {
return await _request(() async {
final request = DynSpaceReq()
..hostUid = Int64(uid)
..localTime = 8
..page = Int64(page)
..from = 'space';
final DynSpaceRsp response = await GrpcClient.instance.dynamicClientV2
.dynSpace(request, options: options);
return {'status': true, 'data': response};
});
return await _request(
GrpcUrl.dynSpace,
DynSpaceReq(
hostUid: Int64(uid),
localTime: 8,
page: Int64(page),
from: 'space'),
DynSpaceRsp.fromBuffer);
}
static Future dynRed() async {
return await _request(() async {
final request = DynRedReq()..tabOffset.add(TabOffset(tab: 1));
final DynRedReply response = await GrpcClient.instance.dynamicClientV1
.dynRed(request, options: options);
return {'status': true, 'data': response.dynRedItem.count.toInt()};
});
return await _request(GrpcUrl.dynRed,
DynRedReq(tabOffset: [TabOffset(tab: 1)]), DynRedReply.fromBuffer,
onSuccess: (response) => response.dynRedItem.count.toInt());
}
static Future dmSegMobile(
{required int cid, required int segmentIndex, int type = 1}) async {
return await _request(
GrpcUrl.dmSegMobile,
DmSegMobileReq(
oid: Int64(cid), segmentIndex: Int64(segmentIndex), type: type),
DmSegMobileReply.fromBuffer);
}
}