mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 00:28:18 +08:00
opt: isolate parse danmaku & feat: grpc account (#1785)
* opt: isolate parse danmaku * feat: grpc account
This commit is contained in:
committed by
GitHub
parent
1e0e2d2d6e
commit
b4a5d985f5
@@ -18,6 +18,7 @@ abstract final class DmGrpc {
|
|||||||
type: type,
|
type: type,
|
||||||
),
|
),
|
||||||
DmSegMobileReply.fromBuffer,
|
DmSegMobileReply.fromBuffer,
|
||||||
|
isolate: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,120 +1,19 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/metadata.pb.dart';
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/metadata/device.pb.dart';
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/metadata/fawkes.pb.dart';
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/metadata/locale.pb.dart';
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/metadata/network.pb.dart' as network;
|
|
||||||
import 'package:PiliPlus/grpc/bilibili/rpc.pb.dart';
|
import 'package:PiliPlus/grpc/bilibili/rpc.pb.dart';
|
||||||
import 'package:PiliPlus/http/constants.dart';
|
import 'package:PiliPlus/http/constants.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/utils/accounts.dart';
|
|
||||||
import 'package:PiliPlus/utils/id_utils.dart';
|
|
||||||
import 'package:PiliPlus/utils/login_utils.dart';
|
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
|
||||||
import 'package:archive/archive.dart';
|
import 'package:archive/archive.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
import 'package:flutter/foundation.dart' show kDebugMode, compute;
|
||||||
import 'package:protobuf/protobuf.dart' show GeneratedMessage;
|
import 'package:protobuf/protobuf.dart' show GeneratedMessage;
|
||||||
|
|
||||||
abstract final class GrpcReq {
|
abstract final class GrpcReq {
|
||||||
static String? _accessKey = Accounts.main.accessKey;
|
static const _isolateSize = 256 * 1024;
|
||||||
static const _build = 2001100;
|
|
||||||
static const _versionName = '2.0.1';
|
|
||||||
static const _biliChannel = 'master';
|
|
||||||
static const _mobiApp = 'android_hd';
|
|
||||||
static const _device = 'android';
|
|
||||||
|
|
||||||
static final _buvid = LoginUtils.buvid;
|
static final options = Options(responseType: ResponseType.bytes);
|
||||||
static final _traceId = IdUtils.genTraceId();
|
|
||||||
static final _sessionId = Utils.generateRandomString(8);
|
|
||||||
|
|
||||||
static void updateHeaders(String? accessKey) {
|
|
||||||
_accessKey = accessKey;
|
|
||||||
if (_accessKey != null) {
|
|
||||||
headers['authorization'] = 'identify_v1 $_accessKey';
|
|
||||||
} else {
|
|
||||||
headers.remove('authorization');
|
|
||||||
}
|
|
||||||
headers['x-bili-metadata-bin'] = base64Encode(
|
|
||||||
Metadata(
|
|
||||||
accessKey: _accessKey ?? '',
|
|
||||||
mobiApp: _mobiApp,
|
|
||||||
device: _device,
|
|
||||||
build: _build,
|
|
||||||
channel: _biliChannel,
|
|
||||||
buvid: _buvid,
|
|
||||||
platform: _device,
|
|
||||||
).writeToBuffer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Map<String, String> headers = {
|
|
||||||
Headers.contentTypeHeader: 'application/grpc',
|
|
||||||
'grpc-encoding': 'gzip',
|
|
||||||
'gzip-accept-encoding': 'gzip,identity',
|
|
||||||
'user-agent': Constants.userAgent,
|
|
||||||
'x-bili-gaia-vtoken': '',
|
|
||||||
'x-bili-aurora-zone': '',
|
|
||||||
'x-bili-trace-id': _traceId,
|
|
||||||
if (_accessKey != null) 'authorization': 'identify_v1 $_accessKey',
|
|
||||||
'buvid': _buvid,
|
|
||||||
'bili-http-engine': 'cronet',
|
|
||||||
'te': 'trailers',
|
|
||||||
'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: _device,
|
|
||||||
build: _build,
|
|
||||||
channel: _biliChannel,
|
|
||||||
buvid: _buvid,
|
|
||||||
platform: _device,
|
|
||||||
).writeToBuffer(),
|
|
||||||
),
|
|
||||||
'x-bili-device-bin': base64Encode(
|
|
||||||
Device(
|
|
||||||
appId: 5,
|
|
||||||
build: _build,
|
|
||||||
buvid: _buvid,
|
|
||||||
mobiApp: _mobiApp,
|
|
||||||
platform: _device,
|
|
||||||
channel: _biliChannel,
|
|
||||||
brand: _device,
|
|
||||||
model: _device,
|
|
||||||
osver: '15',
|
|
||||||
versionName: _versionName,
|
|
||||||
).writeToBuffer(),
|
|
||||||
),
|
|
||||||
'x-bili-network-bin': base64Encode(
|
|
||||||
network.Network(
|
|
||||||
type: network.NetworkType.WIFI,
|
|
||||||
).writeToBuffer(),
|
|
||||||
),
|
|
||||||
'x-bili-locale-bin': base64Encode(
|
|
||||||
Locale(
|
|
||||||
cLocale: LocaleIds(language: 'zh', region: 'CN', script: 'Hans'),
|
|
||||||
sLocale: LocaleIds(language: 'zh', region: 'CN', script: 'Hans'),
|
|
||||||
timezone: 'Asia/Shanghai',
|
|
||||||
).writeToBuffer(),
|
|
||||||
),
|
|
||||||
'x-bili-exps-bin': '',
|
|
||||||
};
|
|
||||||
|
|
||||||
static final Options options = Options(
|
|
||||||
headers: headers,
|
|
||||||
responseType: ResponseType.bytes,
|
|
||||||
);
|
|
||||||
|
|
||||||
static Uint8List compressProtobuf(Uint8List proto) {
|
static Uint8List compressProtobuf(Uint8List proto) {
|
||||||
proto = const GZipEncoder().encodeBytes(proto);
|
proto = const GZipEncoder().encodeBytes(proto);
|
||||||
@@ -136,11 +35,22 @@ abstract final class GrpcReq {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LoadingState<T> _parse<T>((Uint8List, T Function(Uint8List)) args) {
|
||||||
|
try {
|
||||||
|
final data = decompressProtobuf(args.$1);
|
||||||
|
final grpcResponse = args.$2(data);
|
||||||
|
return Success(grpcResponse);
|
||||||
|
} catch (e) {
|
||||||
|
return Error(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<LoadingState<T>> request<T>(
|
static Future<LoadingState<T>> request<T>(
|
||||||
String url,
|
String url,
|
||||||
GeneratedMessage request,
|
GeneratedMessage request,
|
||||||
T Function(Uint8List) grpcParser,
|
T Function(Uint8List) grpcParser, {
|
||||||
) async {
|
bool isolate = false,
|
||||||
|
}) async {
|
||||||
final response = await Request().post<Uint8List>(
|
final response = await Request().post<Uint8List>(
|
||||||
HttpString.appBaseUrl + url,
|
HttpString.appBaseUrl + url,
|
||||||
data: compressProtobuf(request.writeToBuffer()),
|
data: compressProtobuf(request.writeToBuffer()),
|
||||||
@@ -152,13 +62,13 @@ abstract final class GrpcReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.headers.value('Grpc-Status') == '0') {
|
if (response.headers.value('Grpc-Status') == '0') {
|
||||||
try {
|
final data = response.data;
|
||||||
Uint8List data = response.data;
|
if (data is Uint8List) {
|
||||||
data = decompressProtobuf(data);
|
return isolate && data.length > _isolateSize
|
||||||
final grpcResponse = grpcParser(data);
|
? compute(_parse, (data, grpcParser))
|
||||||
return Success(grpcResponse);
|
: _parse((data, grpcParser));
|
||||||
} catch (e) {
|
} else {
|
||||||
return Error(e.toString());
|
return Error('grpc: ${data.runtimeType} is not Uint8List');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:PiliPlus/models/common/account_type.dart';
|
|||||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||||
import 'package:PiliPlus/pages/setting/models/model.dart';
|
import 'package:PiliPlus/pages/setting/models/model.dart';
|
||||||
import 'package:PiliPlus/utils/accounts.dart';
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
import 'package:PiliPlus/utils/accounts/account_manager/account_mgr.dart';
|
import 'package:PiliPlus/utils/accounts/api_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
@@ -61,7 +61,7 @@ Widget _getAccountDetail(BuildContext context) {
|
|||||||
final slivers = <Widget>[];
|
final slivers = <Widget>[];
|
||||||
final theme = TextTheme.of(context);
|
final theme = TextTheme.of(context);
|
||||||
for (var i in AccountType.values) {
|
for (var i in AccountType.values) {
|
||||||
final url = AccountManager.apiTypeSet[i];
|
final url = ApiType.apiTypeSet[i];
|
||||||
if (url == null) continue;
|
if (url == null) continue;
|
||||||
|
|
||||||
slivers
|
slivers
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
import 'package:PiliPlus/models/common/account_type.dart';
|
import 'package:PiliPlus/models/common/account_type.dart';
|
||||||
import 'package:PiliPlus/utils/accounts.dart';
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
|
import 'package:PiliPlus/utils/accounts/grpc_headers.dart';
|
||||||
import 'package:PiliPlus/utils/id_utils.dart';
|
import 'package:PiliPlus/utils/id_utils.dart';
|
||||||
import 'package:cookie_jar/cookie_jar.dart';
|
import 'package:cookie_jar/cookie_jar.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@@ -26,6 +27,8 @@ sealed class Account {
|
|||||||
|
|
||||||
Map<String, String> get headers => throw UnimplementedError();
|
Map<String, String> get headers => throw UnimplementedError();
|
||||||
|
|
||||||
|
Map<String, String> get grpcHeaders => throw UnimplementedError();
|
||||||
|
|
||||||
bool get isLogin => throw UnimplementedError();
|
bool get isLogin => throw UnimplementedError();
|
||||||
|
|
||||||
int get mid => throw UnimplementedError();
|
int get mid => throw UnimplementedError();
|
||||||
@@ -65,6 +68,11 @@ class LoginAccount extends Account {
|
|||||||
'x-bili-aurora-eid': IdUtils.genAuroraEid(mid),
|
'x-bili-aurora-eid': IdUtils.genAuroraEid(mid),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
late final Map<String, String> grpcHeaders = GrpcHeaders.newHeaders(
|
||||||
|
accessKey,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
late final String csrf =
|
late final String csrf =
|
||||||
cookieJar.domainCookies['bilibili.com']!['/']!['bili_jct']!.cookie.value;
|
cookieJar.domainCookies['bilibili.com']!['/']!['bili_jct']!.cookie.value;
|
||||||
@@ -140,12 +148,17 @@ class AnonymousAccount extends Account {
|
|||||||
@override
|
@override
|
||||||
final Map<String, String> headers = Constants.baseHeaders;
|
final Map<String, String> headers = Constants.baseHeaders;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Map<String, String> grpcHeaders = GrpcHeaders.newHeaders();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool activated = false;
|
bool activated = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> delete() =>
|
Future<void> delete() {
|
||||||
cookieJar.deleteAll().whenComplete(cookieJar.setBuvid3);
|
grpcHeaders['x-bili-fawkes-req-bin'] = GrpcHeaders.fawkes;
|
||||||
|
return cookieJar.deleteAll().whenComplete(cookieJar.setBuvid3);
|
||||||
|
}
|
||||||
|
|
||||||
static final _instance = AnonymousAccount._();
|
static final _instance = AnonymousAccount._();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:PiliPlus/http/constants.dart';
|
|||||||
import 'package:PiliPlus/models/common/account_type.dart';
|
import 'package:PiliPlus/models/common/account_type.dart';
|
||||||
import 'package:PiliPlus/utils/accounts.dart';
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||||
|
import 'package:PiliPlus/utils/accounts/api_type.dart';
|
||||||
import 'package:PiliPlus/utils/app_sign.dart';
|
import 'package:PiliPlus/utils/app_sign.dart';
|
||||||
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
||||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||||
@@ -20,115 +21,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|||||||
final _setCookieReg = RegExp('(?<=)(,)(?=[^;]+?=)');
|
final _setCookieReg = RegExp('(?<=)(,)(?=[^;]+?=)');
|
||||||
|
|
||||||
class AccountManager extends Interceptor {
|
class AccountManager extends Interceptor {
|
||||||
static const Map<AccountType, Set<String>> apiTypeSet = {
|
|
||||||
AccountType.heartbeat: {
|
|
||||||
Api.videoIntro,
|
|
||||||
Api.replyList,
|
|
||||||
Api.replyReplyList,
|
|
||||||
|
|
||||||
// history
|
|
||||||
Api.heartBeat,
|
|
||||||
Api.historyReport,
|
|
||||||
Api.roomEntryAction,
|
|
||||||
Api.liveLikeReport,
|
|
||||||
Api.mediaListHistory,
|
|
||||||
// Api.historyList,
|
|
||||||
// Api.pauseHistory,
|
|
||||||
// Api.clearHistory,
|
|
||||||
// Api.delHistory,
|
|
||||||
// Api.searchHistory,
|
|
||||||
// Api.historyStatus,
|
|
||||||
// progress
|
|
||||||
Api.pgcInfo,
|
|
||||||
Api.pugvInfo,
|
|
||||||
|
|
||||||
Api.ab2c,
|
|
||||||
Api.liveRoomInfo,
|
|
||||||
Api.liveRoomInfoH5,
|
|
||||||
Api.onlineTotal,
|
|
||||||
Api.dynamicDetail,
|
|
||||||
Api.aiConclusion,
|
|
||||||
Api.getSeasonDetailApi,
|
|
||||||
Api.liveRoomDmToken,
|
|
||||||
Api.liveRoomDmPrefetch,
|
|
||||||
Api.superChatMsg,
|
|
||||||
Api.searchByType,
|
|
||||||
Api.dynSearch,
|
|
||||||
Api.searchArchive,
|
|
||||||
|
|
||||||
// Api.memberInfo,
|
|
||||||
// Api.bgmDetail,
|
|
||||||
// Api.space,
|
|
||||||
// Api.spaceAudio,
|
|
||||||
// Api.spaceComic,
|
|
||||||
// Api.spaceArchive,
|
|
||||||
// Api.spaceChargingArchive,
|
|
||||||
// Api.spaceSeason,
|
|
||||||
// Api.spaceSeries,
|
|
||||||
// Api.spaceBangumi,
|
|
||||||
// Api.spaceOpus,
|
|
||||||
// Api.spaceFav,
|
|
||||||
// Api.seasonSeries,
|
|
||||||
// Api.matchInfo,
|
|
||||||
// Api.articleList,
|
|
||||||
// Api.opusDetail,
|
|
||||||
// Api.articleView,
|
|
||||||
// Api.articleInfo,
|
|
||||||
},
|
|
||||||
AccountType.recommend: {
|
|
||||||
Api.recommendListWeb,
|
|
||||||
Api.recommendListApp,
|
|
||||||
Api.feedDislike,
|
|
||||||
Api.feedDislikeCancel,
|
|
||||||
Api.hotList,
|
|
||||||
Api.relatedList,
|
|
||||||
Api.hotSearchList, // 不同账号搜索结果可能不一样
|
|
||||||
Api.searchDefault,
|
|
||||||
Api.searchSuggest,
|
|
||||||
Api.liveList,
|
|
||||||
Api.searchTrending,
|
|
||||||
Api.searchRecommend,
|
|
||||||
Api.getRankApi,
|
|
||||||
Api.pgcRank,
|
|
||||||
Api.pgcSeasonRank,
|
|
||||||
Api.pgcIndexResult,
|
|
||||||
Api.popularSeriesOne,
|
|
||||||
Api.popularSeriesList,
|
|
||||||
Api.popularPrecious,
|
|
||||||
Api.liveAreaList,
|
|
||||||
Api.liveFeedIndex,
|
|
||||||
Api.liveSecondList,
|
|
||||||
Api.liveRoomAreaList,
|
|
||||||
Api.liveSearch,
|
|
||||||
Api.bgmRecommend,
|
|
||||||
Api.dynTopicRcmd,
|
|
||||||
Api.topicFeed,
|
|
||||||
Api.topicTop,
|
|
||||||
},
|
|
||||||
// progress
|
|
||||||
AccountType.video: {
|
|
||||||
Api.ugcUrl,
|
|
||||||
Api.pgcUrl,
|
|
||||||
Api.pugvUrl,
|
|
||||||
Api.tvPlayUrl,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const loginApi = {
|
|
||||||
Api.getTVCode,
|
|
||||||
Api.qrcodePoll,
|
|
||||||
Api.getCaptcha,
|
|
||||||
Api.getWebKey,
|
|
||||||
Api.appSmsCode,
|
|
||||||
Api.loginByPwdApi,
|
|
||||||
Api.logInByAppSms,
|
|
||||||
Api.safeCenterGetInfo,
|
|
||||||
Api.preCapture,
|
|
||||||
Api.safeCenterSmsCode,
|
|
||||||
Api.safeCenterSmsVerify,
|
|
||||||
Api.oauth2AccessToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountManager();
|
AccountManager();
|
||||||
|
|
||||||
String blockServer = Pref.blockServer;
|
String blockServer = Pref.blockServer;
|
||||||
@@ -164,20 +56,23 @@ class AccountManager extends Interceptor {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final isApp = path.startsWith(HttpString.appBaseUrl);
|
||||||
|
|
||||||
|
if (isApp && options.responseType == ResponseType.bytes) {
|
||||||
|
options.headers.addAll(account.grpcHeaders);
|
||||||
|
return handler.next(options);
|
||||||
|
}
|
||||||
|
|
||||||
options.headers
|
options.headers
|
||||||
..addAll(account.headers)
|
..addAll(account.headers)
|
||||||
..['referer'] ??= HttpString.baseUrl;
|
..['referer'] ??= HttpString.baseUrl;
|
||||||
|
|
||||||
// app端不需要管理cookie
|
// app端不需要管理cookie
|
||||||
if (path.startsWith(HttpString.appBaseUrl)) {
|
if (isApp) {
|
||||||
// if (kDebugMode) debugPrint('is app: ${options.path}');
|
// if (kDebugMode) debugPrint('is app: ${options.path}');
|
||||||
// bytes是grpc响应
|
final dataPtr = (options.method == 'POST' && options.data is Map
|
||||||
if (options.responseType != ResponseType.bytes) {
|
? (options.data as Map).cast<String, dynamic>()
|
||||||
final dataPtr =
|
: options.queryParameters);
|
||||||
(options.method == 'POST' && options.data is Map
|
|
||||||
? options.data as Map
|
|
||||||
: options.queryParameters)
|
|
||||||
.cast<String, dynamic>();
|
|
||||||
if (dataPtr.isNotEmpty) {
|
if (dataPtr.isNotEmpty) {
|
||||||
if (!account.accessKey.isNullOrEmpty) {
|
if (!account.accessKey.isNullOrEmpty) {
|
||||||
dataPtr['access_key'] = account.accessKey!;
|
dataPtr['access_key'] = account.accessKey!;
|
||||||
@@ -187,7 +82,6 @@ class AccountManager extends Interceptor {
|
|||||||
AppSign.appSign(dataPtr);
|
AppSign.appSign(dataPtr);
|
||||||
// if (kDebugMode) debugPrint(dataPtr.toString());
|
// if (kDebugMode) debugPrint(dataPtr.toString());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return handler.next(options);
|
return handler.next(options);
|
||||||
} else {
|
} else {
|
||||||
account.cookieJar
|
account.cookieJar
|
||||||
@@ -222,9 +116,9 @@ class AccountManager extends Interceptor {
|
|||||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||||
final options = response.requestOptions;
|
final options = response.requestOptions;
|
||||||
final path = options.path;
|
final path = options.path;
|
||||||
if (path.startsWith(HttpString.appBaseUrl) ||
|
if (options.extra['account'] is NoAccount ||
|
||||||
_skipCookie(path) ||
|
path.startsWith(HttpString.appBaseUrl) ||
|
||||||
options.extra['account'] is NoAccount) {
|
_skipCookie(path)) {
|
||||||
return handler.next(response);
|
return handler.next(response);
|
||||||
} else {
|
} else {
|
||||||
final future = _saveCookies(
|
final future = _saveCookies(
|
||||||
@@ -309,7 +203,7 @@ class AccountManager extends Interceptor {
|
|||||||
.map(Cookie.fromSetCookieValue)
|
.map(Cookie.fromSetCookieValue)
|
||||||
.toList();
|
.toList();
|
||||||
final statusCode = response.statusCode ?? 0;
|
final statusCode = response.statusCode ?? 0;
|
||||||
final locations = response.headers[HttpHeaders.locationHeader] ?? [];
|
final locations = response.headers[HttpHeaders.locationHeader] ?? const [];
|
||||||
final isRedirectRequest = statusCode >= 300 && statusCode < 400;
|
final isRedirectRequest = statusCode >= 300 && statusCode < 400;
|
||||||
final originalUri = response.requestOptions.uri;
|
final originalUri = response.requestOptions.uri;
|
||||||
final realUri = originalUri.resolveUri(response.realUri);
|
final realUri = originalUri.resolveUri(response.realUri);
|
||||||
@@ -335,11 +229,11 @@ class AccountManager extends Interceptor {
|
|||||||
path.contains('biliimg.com');
|
path.contains('biliimg.com');
|
||||||
}
|
}
|
||||||
|
|
||||||
Account _findAccount(String path) => loginApi.contains(path)
|
Account _findAccount(String path) => ApiType.loginApi.contains(path)
|
||||||
? AnonymousAccount()
|
? AnonymousAccount()
|
||||||
: Accounts.get(
|
: Accounts.get(
|
||||||
AccountType.values.firstWhere(
|
AccountType.values.firstWhere(
|
||||||
(i) => apiTypeSet[i]?.contains(path) == true,
|
(i) => ApiType.apiTypeSet[i]?.contains(path) == true,
|
||||||
orElse: () => AccountType.main,
|
orElse: () => AccountType.main,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
114
lib/utils/accounts/api_type.dart
Normal file
114
lib/utils/accounts/api_type.dart
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import 'package:PiliPlus/http/api.dart';
|
||||||
|
import 'package:PiliPlus/models/common/account_type.dart';
|
||||||
|
|
||||||
|
abstract final class ApiType {
|
||||||
|
// TODO: grpc api type
|
||||||
|
static const Map<AccountType, Set<String>> apiTypeSet = {
|
||||||
|
AccountType.heartbeat: {
|
||||||
|
Api.videoIntro,
|
||||||
|
Api.replyList,
|
||||||
|
Api.replyReplyList,
|
||||||
|
|
||||||
|
// history
|
||||||
|
Api.heartBeat,
|
||||||
|
Api.historyReport,
|
||||||
|
Api.roomEntryAction,
|
||||||
|
Api.liveLikeReport,
|
||||||
|
Api.mediaListHistory,
|
||||||
|
// Api.historyList,
|
||||||
|
// Api.pauseHistory,
|
||||||
|
// Api.clearHistory,
|
||||||
|
// Api.delHistory,
|
||||||
|
// Api.searchHistory,
|
||||||
|
// Api.historyStatus,
|
||||||
|
// progress
|
||||||
|
Api.pgcInfo,
|
||||||
|
Api.pugvInfo,
|
||||||
|
|
||||||
|
Api.ab2c,
|
||||||
|
Api.liveRoomInfo,
|
||||||
|
Api.liveRoomInfoH5,
|
||||||
|
Api.onlineTotal,
|
||||||
|
Api.dynamicDetail,
|
||||||
|
Api.aiConclusion,
|
||||||
|
Api.getSeasonDetailApi,
|
||||||
|
Api.liveRoomDmToken,
|
||||||
|
Api.liveRoomDmPrefetch,
|
||||||
|
Api.superChatMsg,
|
||||||
|
Api.searchByType,
|
||||||
|
Api.dynSearch,
|
||||||
|
Api.searchArchive,
|
||||||
|
|
||||||
|
// Api.memberInfo,
|
||||||
|
// Api.bgmDetail,
|
||||||
|
// Api.space,
|
||||||
|
// Api.spaceAudio,
|
||||||
|
// Api.spaceComic,
|
||||||
|
// Api.spaceArchive,
|
||||||
|
// Api.spaceChargingArchive,
|
||||||
|
// Api.spaceSeason,
|
||||||
|
// Api.spaceSeries,
|
||||||
|
// Api.spaceBangumi,
|
||||||
|
// Api.spaceOpus,
|
||||||
|
// Api.spaceFav,
|
||||||
|
// Api.seasonSeries,
|
||||||
|
// Api.matchInfo,
|
||||||
|
// Api.articleList,
|
||||||
|
// Api.opusDetail,
|
||||||
|
// Api.articleView,
|
||||||
|
// Api.articleInfo,
|
||||||
|
},
|
||||||
|
AccountType.recommend: {
|
||||||
|
Api.recommendListWeb,
|
||||||
|
Api.recommendListApp,
|
||||||
|
Api.feedDislike,
|
||||||
|
Api.feedDislikeCancel,
|
||||||
|
Api.hotList,
|
||||||
|
Api.relatedList,
|
||||||
|
Api.hotSearchList, // 不同账号搜索结果可能不一样
|
||||||
|
Api.searchDefault,
|
||||||
|
Api.searchSuggest,
|
||||||
|
Api.liveList,
|
||||||
|
Api.searchTrending,
|
||||||
|
Api.searchRecommend,
|
||||||
|
Api.getRankApi,
|
||||||
|
Api.pgcRank,
|
||||||
|
Api.pgcSeasonRank,
|
||||||
|
Api.pgcIndexResult,
|
||||||
|
Api.popularSeriesOne,
|
||||||
|
Api.popularSeriesList,
|
||||||
|
Api.popularPrecious,
|
||||||
|
Api.liveAreaList,
|
||||||
|
Api.liveFeedIndex,
|
||||||
|
Api.liveSecondList,
|
||||||
|
Api.liveRoomAreaList,
|
||||||
|
Api.liveSearch,
|
||||||
|
Api.bgmRecommend,
|
||||||
|
Api.dynTopicRcmd,
|
||||||
|
Api.topicFeed,
|
||||||
|
Api.topicTop,
|
||||||
|
},
|
||||||
|
// progress
|
||||||
|
AccountType.video: {
|
||||||
|
Api.ugcUrl,
|
||||||
|
Api.pgcUrl,
|
||||||
|
Api.pugvUrl,
|
||||||
|
Api.tvPlayUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const loginApi = {
|
||||||
|
Api.getTVCode,
|
||||||
|
Api.qrcodePoll,
|
||||||
|
Api.getCaptcha,
|
||||||
|
Api.getWebKey,
|
||||||
|
Api.appSmsCode,
|
||||||
|
Api.loginByPwdApi,
|
||||||
|
Api.logInByAppSms,
|
||||||
|
Api.safeCenterGetInfo,
|
||||||
|
Api.preCapture,
|
||||||
|
Api.safeCenterSmsCode,
|
||||||
|
Api.safeCenterSmsVerify,
|
||||||
|
Api.oauth2AccessToken,
|
||||||
|
};
|
||||||
|
}
|
||||||
88
lib/utils/accounts/grpc_headers.dart
Normal file
88
lib/utils/accounts/grpc_headers.dart
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/grpc/bilibili/metadata.pb.dart';
|
||||||
|
import 'package:PiliPlus/grpc/bilibili/metadata/device.pb.dart';
|
||||||
|
import 'package:PiliPlus/grpc/bilibili/metadata/fawkes.pb.dart';
|
||||||
|
import 'package:PiliPlus/grpc/bilibili/metadata/locale.pb.dart';
|
||||||
|
import 'package:PiliPlus/grpc/bilibili/metadata/network.pb.dart' as network;
|
||||||
|
import 'package:PiliPlus/utils/login_utils.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
abstract final class GrpcHeaders {
|
||||||
|
static const _build = 2001100;
|
||||||
|
static const _versionName = '2.0.1';
|
||||||
|
static const _biliChannel = 'master';
|
||||||
|
static const _mobiApp = 'android_hd';
|
||||||
|
static const _device = 'android';
|
||||||
|
|
||||||
|
static String get _buvid => LoginUtils.buvid;
|
||||||
|
static String get _traceId => Constants.traceId;
|
||||||
|
static String get _sessionId => Utils.generateRandomString(8);
|
||||||
|
|
||||||
|
static final Map<String, String> _base = {
|
||||||
|
Headers.contentTypeHeader: 'application/grpc',
|
||||||
|
'grpc-encoding': 'gzip',
|
||||||
|
'gzip-accept-encoding': 'gzip,identity',
|
||||||
|
'user-agent': Constants.userAgent,
|
||||||
|
'x-bili-gaia-vtoken': '',
|
||||||
|
'x-bili-aurora-zone': '',
|
||||||
|
'x-bili-trace-id': _traceId,
|
||||||
|
'buvid': _buvid,
|
||||||
|
'bili-http-engine': 'cronet',
|
||||||
|
// 'te': 'trailers', // dio not supported
|
||||||
|
'x-bili-device-bin': base64Encode(
|
||||||
|
Device(
|
||||||
|
appId: 5,
|
||||||
|
build: _build,
|
||||||
|
buvid: _buvid,
|
||||||
|
mobiApp: _mobiApp,
|
||||||
|
platform: _device,
|
||||||
|
channel: _biliChannel,
|
||||||
|
brand: _device,
|
||||||
|
model: _device,
|
||||||
|
osver: '15',
|
||||||
|
versionName: _versionName,
|
||||||
|
).writeToBuffer(),
|
||||||
|
),
|
||||||
|
'x-bili-network-bin': base64Encode(
|
||||||
|
network.Network(type: network.NetworkType.WIFI).writeToBuffer(),
|
||||||
|
),
|
||||||
|
'x-bili-locale-bin': base64Encode(
|
||||||
|
Locale(
|
||||||
|
cLocale: LocaleIds(language: 'zh', region: 'CN', script: 'Hans'),
|
||||||
|
sLocale: LocaleIds(language: 'zh', region: 'CN', script: 'Hans'),
|
||||||
|
timezone: 'Asia/Shanghai',
|
||||||
|
).writeToBuffer(),
|
||||||
|
),
|
||||||
|
'x-bili-exps-bin': '',
|
||||||
|
};
|
||||||
|
|
||||||
|
static String get fawkes => base64Encode(
|
||||||
|
FawkesReq(
|
||||||
|
appkey: _mobiApp,
|
||||||
|
env: 'prod',
|
||||||
|
sessionId: _sessionId,
|
||||||
|
).writeToBuffer(),
|
||||||
|
);
|
||||||
|
|
||||||
|
static Map<String, String> newHeaders([String? accessKey]) {
|
||||||
|
return {
|
||||||
|
..._base,
|
||||||
|
if (accessKey != null) 'authorization': 'identify_v1 $accessKey',
|
||||||
|
'x-bili-fawkes-req-bin': fawkes,
|
||||||
|
'x-bili-metadata-bin': base64Encode(
|
||||||
|
Metadata(
|
||||||
|
accessKey: accessKey,
|
||||||
|
mobiApp: _mobiApp,
|
||||||
|
device: _device,
|
||||||
|
build: _build,
|
||||||
|
channel: _biliChannel,
|
||||||
|
buvid: _buvid,
|
||||||
|
platform: _device,
|
||||||
|
).writeToBuffer(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,19 +96,15 @@ abstract final class IdUtils {
|
|||||||
return base64Encoded;
|
return base64Encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/grpc_api/readme.md#x-bili-trace-id-生成算法
|
||||||
static String genTraceId() {
|
static String genTraceId() {
|
||||||
String randomId = Utils.generateRandomString(32);
|
final randomTraceId = StringBuffer(Utils.generateRandomString(24));
|
||||||
|
|
||||||
StringBuffer randomTraceId = StringBuffer(randomId.substring(0, 24));
|
final ts = (DateTime.now().millisecondsSinceEpoch ~/ 1000) >> 8;
|
||||||
|
|
||||||
int ts = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
randomTraceId
|
||||||
|
..write((ts & 0xFFFFFF).toRadixString(16).padLeft(6, '0'))
|
||||||
for (int i = 2; i >= 0; i--) {
|
..write(Utils.generateRandomString(2));
|
||||||
ts >>= 8;
|
|
||||||
randomTraceId.write((ts & 0xFF).toRadixString(16).padLeft(2, '0'));
|
|
||||||
}
|
|
||||||
|
|
||||||
randomTraceId.write(randomId.substring(30, 32));
|
|
||||||
|
|
||||||
return '${randomTraceId.toString()}:${randomTraceId.toString().substring(16, 32)}:0:0';
|
return '${randomTraceId.toString()}:${randomTraceId.toString().substring(16, 32)}:0:0';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:async' show FutureOr;
|
import 'dart:async' show FutureOr;
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
import 'package:PiliPlus/grpc/grpc_req.dart';
|
|
||||||
import 'package:PiliPlus/http/user.dart';
|
import 'package:PiliPlus/http/user.dart';
|
||||||
import 'package:PiliPlus/main.dart';
|
import 'package:PiliPlus/main.dart';
|
||||||
import 'package:PiliPlus/models/user/info.dart';
|
import 'package:PiliPlus/models/user/info.dart';
|
||||||
@@ -49,7 +48,6 @@ abstract final class LoginUtils {
|
|||||||
final account = Accounts.main;
|
final account = Accounts.main;
|
||||||
final result = await UserHttp.userInfo();
|
final result = await UserHttp.userInfo();
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
GrpcReq.updateHeaders(account.accessKey);
|
|
||||||
setWebCookie(account);
|
setWebCookie(account);
|
||||||
RequestUtils.syncHistoryStatus();
|
RequestUtils.syncHistoryStatus();
|
||||||
final UserInfoData data = result.data;
|
final UserInfoData data = result.data;
|
||||||
@@ -83,8 +81,6 @@ abstract final class LoginUtils {
|
|||||||
..face.value = ''
|
..face.value = ''
|
||||||
..isLogin.value = false;
|
..isLogin.value = false;
|
||||||
|
|
||||||
GrpcReq.updateHeaders(null);
|
|
||||||
|
|
||||||
return Future.wait([
|
return Future.wait([
|
||||||
if (!Platform.isLinux)
|
if (!Platform.isLinux)
|
||||||
web.CookieManager.instance(
|
web.CookieManager.instance(
|
||||||
|
|||||||
Reference in New Issue
Block a user