opt: buvid

This commit is contained in:
My-Responsitories
2025-09-23 12:20:07 +08:00
parent d727e2a62a
commit da1ea68f8b
11 changed files with 102 additions and 62 deletions

View File

@@ -13,7 +13,6 @@ 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/accounts.dart';
import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/id_utils.dart';
import 'package:PiliPlus/utils/login_utils.dart';
import 'package:PiliPlus/utils/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';
@@ -21,35 +20,46 @@ import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:protobuf/protobuf.dart' show GeneratedMessage; import 'package:protobuf/protobuf.dart' show GeneratedMessage;
class GrpcReq { class GrpcReq {
static String? _accessKey = Accounts.main.accessKey;
static const _build = 2001100; static const _build = 2001100;
static const _versionName = '2.0.1'; static const _versionName = '2.0.1';
static const _biliChannel = 'master'; static const _biliChannel = 'master';
static const _mobiApp = 'android_hd'; static const _mobiApp = 'android_hd';
static const _device = 'android'; static const _device = 'android';
static final _buvid = LoginUtils.buvid;
static final _traceId = IdUtils.genTraceId(); static final _traceId = IdUtils.genTraceId();
static final _sessionId = Utils.generateRandomString(8); static final _sessionId = Utils.generateRandomString(8);
static void updateHeaders(String? accessKey) { static void updateHeaders(String? accessKey, String buvid) {
_accessKey = accessKey; if (accessKey != null) {
if (_accessKey != null) { headers['authorization'] = 'identify_v1 $accessKey';
headers['authorization'] = 'identify_v1 $_accessKey';
} else { } else {
headers.remove('authorization'); headers.remove('authorization');
} }
headers['x-bili-metadata-bin'] = base64Encode( headers['x-bili-metadata-bin'] = base64Encode(
Metadata( Metadata(
accessKey: _accessKey ?? '', accessKey: accessKey ?? '',
mobiApp: _mobiApp, mobiApp: _mobiApp,
device: _device, device: _device,
build: _build, build: _build,
channel: _biliChannel, channel: _biliChannel,
buvid: _buvid, buvid: buvid,
platform: _device, platform: _device,
).writeToBuffer(), ).writeToBuffer(),
); );
headers['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(),
);
options = Options(headers: headers, responseType: ResponseType.bytes); options = Options(headers: headers, responseType: ResponseType.bytes);
} }
@@ -61,8 +71,9 @@ class GrpcReq {
'x-bili-gaia-vtoken': '', 'x-bili-gaia-vtoken': '',
'x-bili-aurora-zone': '', 'x-bili-aurora-zone': '',
'x-bili-trace-id': _traceId, 'x-bili-trace-id': _traceId,
if (_accessKey != null) 'authorization': 'identify_v1 $_accessKey', if (Accounts.main.accessKey != null)
'buvid': _buvid, 'authorization': 'identify_v1 ${Accounts.main.accessKey}',
'buvid': Accounts.main.buvid,
'bili-http-engine': 'cronet', 'bili-http-engine': 'cronet',
'te': 'trailers', 'te': 'trailers',
'x-bili-fawkes-req-bin': base64Encode( 'x-bili-fawkes-req-bin': base64Encode(
@@ -74,12 +85,12 @@ class GrpcReq {
), ),
'x-bili-metadata-bin': base64Encode( 'x-bili-metadata-bin': base64Encode(
Metadata( Metadata(
accessKey: _accessKey ?? '', accessKey: Accounts.main.accessKey ?? '',
mobiApp: _mobiApp, mobiApp: _mobiApp,
device: _device, device: _device,
build: _build, build: _build,
channel: _biliChannel, channel: _biliChannel,
buvid: _buvid, buvid: Accounts.main.buvid,
platform: _device, platform: _device,
).writeToBuffer(), ).writeToBuffer(),
), ),
@@ -87,7 +98,7 @@ class GrpcReq {
Device( Device(
appId: 5, appId: 5,
build: _build, build: _build,
buvid: _buvid, buvid: Accounts.main.buvid,
mobiApp: _mobiApp, mobiApp: _mobiApp,
platform: _device, platform: _device,
channel: _biliChannel, channel: _biliChannel,
@@ -141,7 +152,7 @@ class GrpcReq {
GeneratedMessage request, GeneratedMessage request,
T Function(Uint8List) grpcParser, T Function(Uint8List) grpcParser,
) async { ) async {
final response = await Request().post( final response = await Request().post<Uint8List>(
HttpString.appBaseUrl + url, HttpString.appBaseUrl + url,
data: compressProtobuf(request.writeToBuffer()), data: compressProtobuf(request.writeToBuffer()),
options: options, options: options,

View File

@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.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/http/login.dart';
import 'package:PiliPlus/http/ua_type.dart'; import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/common/account_type.dart';
import 'package:PiliPlus/models/common/live_search_type.dart'; import 'package:PiliPlus/models/common/live_search_type.dart';
@@ -211,7 +210,7 @@ abstract final class LiveHttp {
queryParameters: params, queryParameters: params,
options: Options( options: Options(
headers: { headers: {
'buvid': LoginHttp.buvid, 'buvid': recommend.buvid,
'fp_local': 'fp_local':
'1111111111111111111111111111111111111111111111111111111111111111', '1111111111111111111111111111111111111111111111111111111111111111',
'fp_remote': 'fp_remote':
@@ -297,7 +296,7 @@ abstract final class LiveHttp {
queryParameters: params, queryParameters: params,
options: Options( options: Options(
headers: { headers: {
'buvid': LoginHttp.buvid, 'buvid': recommend.buvid,
'fp_local': 'fp_local':
'1111111111111111111111111111111111111111111111111111111111111111', '1111111111111111111111111111111111111111111111111111111111111111',
'fp_remote': 'fp_remote':

View File

@@ -17,9 +17,9 @@ import 'package:encrypt/encrypt.dart';
class LoginHttp { class LoginHttp {
static final String deviceId = LoginUtils.genDeviceId(); static final String deviceId = LoginUtils.genDeviceId();
static String get buvid => LoginUtils.buvid; static String get _buvid => AnonymousAccount().buvid;
static final Map<String, String> headers = { static final Map<String, String> headers = {
'buvid': buvid, 'buvid': _buvid,
'env': 'prod', 'env': 'prod',
'app-key': 'android_hd', 'app-key': 'android_hd',
'user-agent': Constants.userAgent, 'user-agent': Constants.userAgent,
@@ -99,7 +99,7 @@ class LoginHttp {
int timestamp = DateTime.now().millisecondsSinceEpoch; int timestamp = DateTime.now().millisecondsSinceEpoch;
var data = { var data = {
'build': '2001100', 'build': '2001100',
'buvid': buvid, 'buvid': _buvid,
'c_locale': 'zh_CN', 'c_locale': 'zh_CN',
'channel': 'master', 'channel': 'master',
'cid': cid, 'cid': cid,
@@ -108,10 +108,10 @@ class LoginHttp {
'gee_challenge': ?geeChallenge, 'gee_challenge': ?geeChallenge,
'gee_seccode': ?geeSeccode, 'gee_seccode': ?geeSeccode,
'gee_validate': ?geeValidate, 'gee_validate': ?geeValidate,
'local_id': buvid, 'local_id': _buvid,
// https://chinggg.github.io/post/appre/ // https://chinggg.github.io/post/appre/
'login_session_id': md5 'login_session_id': md5
.convert(utf8.encode(buvid + timestamp.toString())) .convert(utf8.encode(_buvid + timestamp.toString()))
.toString(), .toString(),
'mobi_app': 'android_hd', 'mobi_app': 'android_hd',
'platform': 'android', 'platform': 'android',
@@ -202,7 +202,7 @@ class LoginHttp {
Map<String, String> data = { Map<String, String> data = {
'bili_local_id': deviceId, 'bili_local_id': deviceId,
'build': '2001100', 'build': '2001100',
'buvid': buvid, 'buvid': _buvid,
'c_locale': 'zh_CN', 'c_locale': 'zh_CN',
'channel': 'master', 'channel': 'master',
'device': 'phone', 'device': 'phone',
@@ -221,7 +221,7 @@ class LoginHttp {
'gee_challenge': ?geeChallenge, 'gee_challenge': ?geeChallenge,
'gee_seccode': ?geeSeccode, 'gee_seccode': ?geeSeccode,
'gee_validate': ?geeValidate, 'gee_validate': ?geeValidate,
'local_id': buvid, //LoginUtils.generateBuvid(), 'local_id': _buvid, //LoginUtils.generateBuvid(),
'mobi_app': 'android_hd', 'mobi_app': 'android_hd',
'password': passwordEncrypted, 'password': passwordEncrypted,
'permission': 'ALL', 'permission': 'ALL',
@@ -271,7 +271,7 @@ class LoginHttp {
Map<String, String> data = { Map<String, String> data = {
'bili_local_id': deviceId, 'bili_local_id': deviceId,
'build': '2001100', 'build': '2001100',
'buvid': buvid, 'buvid': _buvid,
'c_locale': 'zh_CN', 'c_locale': 'zh_CN',
'captcha_key': captchaKey, 'captcha_key': captchaKey,
'channel': 'master', 'channel': 'master',
@@ -291,7 +291,7 @@ class LoginHttp {
), ),
'from_pv': 'main.my-information.my-login.0.click', 'from_pv': 'main.my-information.my-login.0.click',
'from_url': Uri.encodeComponent('bilibili://user_center/mine'), 'from_url': Uri.encodeComponent('bilibili://user_center/mine'),
'local_id': buvid, 'local_id': _buvid,
'mobi_app': 'android_hd', 'mobi_app': 'android_hd',
'platform': 'android', 'platform': 'android',
's_locale': 'zh_CN', 's_locale': 'zh_CN',
@@ -452,7 +452,7 @@ class LoginHttp {
Map<String, String> data = { Map<String, String> data = {
'appkey': Constants.appKey, 'appkey': Constants.appKey,
'build': '2001100', 'build': '2001100',
'buvid': buvid, 'buvid': _buvid,
// 'c_locale': 'zh_CN', // 'c_locale': 'zh_CN',
// 'channel': 'master', // 'channel': 'master',
'code': code, 'code': code,
@@ -462,7 +462,7 @@ class LoginHttp {
// 'device_platform': 'Android14vivo', // 'device_platform': 'Android14vivo',
'disable_rcmd': '0', 'disable_rcmd': '0',
'grant_type': 'authorization_code', 'grant_type': 'authorization_code',
'local_id': buvid, 'local_id': _buvid,
'mobi_app': 'android_hd', 'mobi_app': 'android_hd',
'platform': 'android', 'platform': 'android',
// 's_locale': 'zh_CN', // 's_locale': 'zh_CN',
@@ -505,7 +505,7 @@ class LoginHttp {
static Future<LoadingState<LoginDevicesData>> loginDevices() async { static Future<LoadingState<LoginDevicesData>> loginDevices() async {
final account = Accounts.main; final account = Accounts.main;
final buvid = LoginUtils.buvid; final buvid = account.buvid;
final params = { final params = {
'local_id': buvid, 'local_id': buvid,
'buvid': buvid, 'buvid': buvid,

View File

@@ -4,7 +4,6 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.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/http/login.dart';
import 'package:PiliPlus/http/ua_type.dart'; import 'package:PiliPlus/http/ua_type.dart';
import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/common/account_type.dart';
import 'package:PiliPlus/models/common/video/video_type.dart'; import 'package:PiliPlus/models/common/video/video_type.dart';
@@ -112,7 +111,7 @@ class VideoHttp {
queryParameters: params, queryParameters: params,
options: Options( options: Options(
headers: { headers: {
'buvid': LoginHttp.buvid, 'buvid': Accounts.get(AccountType.recommend).buvid,
'fp_local': 'fp_local':
'1111111111111111111111111111111111111111111111111111111111111111', '1111111111111111111111111111111111111111111111111111111111111111',
'fp_remote': 'fp_remote':

View File

@@ -688,6 +688,8 @@ class LoginPageController extends GetxController
BiliCookieJar.fromList(cookieInfo), BiliCookieJar.fromList(cookieInfo),
tokenInfo['access_token'], tokenInfo['access_token'],
tokenInfo['refresh_token'], tokenInfo['refresh_token'],
{},
AnonymousAccount().buvid,
); );
await Future.wait([account.onChange(), AnonymousAccount().delete()]); await Future.wait([account.onChange(), AnonymousAccount().delete()]);
for (int i = 0; i < AccountType.values.length; i++) { for (int i = 0; i < AccountType.values.length; i++) {

View File

@@ -41,11 +41,9 @@ List<SettingsModel> get privacySettings => [
context: Get.context!, context: Get.context!,
builder: (context) { builder: (context) {
return AlertDialog( return AlertDialog(
title: const Text('查看详情'), title: const Text('账号模式详情'),
content: SingleChildScrollView( content: SingleChildScrollView(
child: Text( child: _getAccountDetail(context),
AccountManager.apiTypeSet[AccountType.heartbeat]!.join('\n'),
),
), ),
actions: [ actions: [
TextButton( TextButton(
@@ -58,7 +56,26 @@ List<SettingsModel> get privacySettings => [
); );
}, },
leading: const Icon(Icons.flag_outlined), leading: const Icon(Icons.flag_outlined),
title: '了解无痕模式', title: '了解账号模式',
subtitle: '查看无痕模式作用的API列表', subtitle: '查看各个账号模式作用的API列表',
), ),
]; ];
Widget _getAccountDetail(BuildContext context) {
final slivers = <Widget>[];
final theme = TextTheme.of(context);
for (var i in AccountType.values) {
final url = AccountManager.apiTypeSet[i];
if (url == null) continue;
slivers
..add(Center(child: Text(i.title, style: theme.titleMedium)))
..add(SelectableText(url.join('\n')));
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: slivers,
);
}

View File

@@ -2,6 +2,7 @@ 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/id_utils.dart'; import 'package:PiliPlus/utils/id_utils.dart';
import 'package:PiliPlus/utils/login_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';
@@ -32,6 +33,8 @@ sealed class Account {
String? get refresh => throw UnimplementedError(); String? get refresh => throw UnimplementedError();
String get buvid => throw UnimplementedError();
const Account(); const Account();
} }
@@ -51,6 +54,9 @@ class LoginAccount extends Account {
@override @override
@HiveField(3) @HiveField(3)
final Set<AccountType> type; final Set<AccountType> type;
@override
@HiveField(4)
final String buvid;
@override @override
bool activited = false; bool activited = false;
@@ -69,11 +75,19 @@ class LoginAccount extends Account {
late final String csrf = late final String csrf =
cookieJar.domainCookies['bilibili.com']!['/']!['bili_jct']!.cookie.value; cookieJar.domainCookies['bilibili.com']!['/']!['bili_jct']!.cookie.value;
@override bool _hasDelete = false;
Future<void> delete() => _box.delete(_midStr);
@override @override
Future<void> onChange() => _box.put(_midStr, this); Future<void> delete() {
assert(_hasDelete = true);
return _box.delete(_midStr);
}
@override
Future<void> onChange() {
assert(!_hasDelete);
return _box.put(_midStr, this);
}
@override @override
Map<String, dynamic>? toJson() => { Map<String, dynamic>? toJson() => {
@@ -81,6 +95,7 @@ class LoginAccount extends Account {
'accessKey': accessKey, 'accessKey': accessKey,
'refresh': refresh, 'refresh': refresh,
'type': type.map((i) => i.index).toList(), 'type': type.map((i) => i.index).toList(),
'buvid': buvid,
}; };
late final String _midStr = cookieJar late final String _midStr = cookieJar
@@ -95,7 +110,9 @@ class LoginAccount extends Account {
this.accessKey, this.accessKey,
this.refresh, [ this.refresh, [
Set<AccountType>? type, Set<AccountType>? type,
]) : type = type ?? {} { String? buvid,
]) : type = type ?? {},
buvid = buvid ?? LoginUtils.generateBuvid() {
cookieJar.setBuvid3(); cookieJar.setBuvid3();
} }
@@ -104,6 +121,7 @@ class LoginAccount extends Account {
json['accessKey'], json['accessKey'],
json['refresh'], json['refresh'],
(json['type'] as Iterable?)?.map((i) => AccountType.values[i]).toSet(), (json['type'] as Iterable?)?.map((i) => AccountType.values[i]).toSet(),
json['buvid'],
); );
@override @override
@@ -131,12 +149,15 @@ class AnonymousAccount extends Account {
final String csrf = ''; final String csrf = '';
@override @override
final Map<String, String> headers = Constants.baseHeaders; final Map<String, String> headers = Constants.baseHeaders;
@override
String buvid = LoginUtils.generateBuvid();
@override @override
bool activited = false; bool activited = false;
@override @override
Future<void> delete() async { Future<void> delete() async {
buvid = LoginUtils.generateBuvid();
await cookieJar.deleteAll(); await cookieJar.deleteAll();
cookieJar.setBuvid3(); cookieJar.setBuvid3();
} }

View File

@@ -18,13 +18,14 @@ class LoginAccountAdapter extends TypeAdapter<LoginAccount> {
fields[1] as String?, fields[1] as String?,
fields[2] as String?, fields[2] as String?,
(fields[3] as List?)?.cast<AccountType>().toSet(), (fields[3] as List?)?.cast<AccountType>().toSet(),
fields[4] as String?,
); );
} }
@override @override
void write(BinaryWriter writer, LoginAccount obj) { void write(BinaryWriter writer, LoginAccount obj) {
writer writer
..writeByte(4) ..writeByte(5)
..writeByte(0) ..writeByte(0)
..write(obj.cookieJar) ..write(obj.cookieJar)
..writeByte(1) ..writeByte(1)
@@ -32,7 +33,9 @@ class LoginAccountAdapter extends TypeAdapter<LoginAccount> {
..writeByte(2) ..writeByte(2)
..write(obj.refresh) ..write(obj.refresh)
..writeByte(3) ..writeByte(3)
..write(obj.type.toList()); ..write(obj.type.toList())
..writeByte(4)
..write(obj.buvid);
} }
@override @override

View File

@@ -49,11 +49,12 @@ abstract class LoginUtils {
static Future<void> onLoginMain() async { static Future<void> onLoginMain() async {
final account = Accounts.main; final account = Accounts.main;
GrpcReq.updateHeaders(account.accessKey);
setWebCookie(account);
RequestUtils.syncHistoryStatus();
final result = await UserHttp.userInfo(); final result = await UserHttp.userInfo();
if (result.isSuccess) { if (result.isSuccess) {
GrpcReq.updateHeaders(account.accessKey, account.buvid);
setWebCookie(account);
RequestUtils.syncHistoryStatus();
final UserInfoData data = result.data; final UserInfoData data = result.data;
if (data.isLogin == true) { if (data.isLogin == true) {
Get.find<AccountService>() Get.find<AccountService>()
@@ -114,7 +115,7 @@ abstract class LoginUtils {
..face.value = '' ..face.value = ''
..isLogin.value = false; ..isLogin.value = false;
GrpcReq.updateHeaders(null); GrpcReq.updateHeaders(null, AnonymousAccount().buvid);
await Future.wait([ await Future.wait([
if (!Platform.isWindows) web.CookieManager.instance().deleteAllCookies(), if (!Platform.isWindows) web.CookieManager.instance().deleteAllCookies(),
@@ -166,8 +167,6 @@ abstract class LoginUtils {
return 'XY${md5Str[2]}${md5Str[12]}${md5Str[22]}$md5Str'; return 'XY${md5Str[2]}${md5Str[12]}${md5Str[22]}$md5Str';
} }
static final buvid = Pref.buvid;
// static String getUUID() { // static String getUUID() {
// return const Uuid().v4().replaceAll('-', ''); // return const Uuid().v4().replaceAll('-', '');
// } // }

View File

@@ -221,8 +221,7 @@ abstract class LocalCacheKey {
blackMids = 'blackMids', blackMids = 'blackMids',
danmakuFilterRules = 'danmakuFilterRules', danmakuFilterRules = 'danmakuFilterRules',
mixinKey = 'mixinKey', mixinKey = 'mixinKey',
timeStamp = 'timeStamp', timeStamp = 'timeStamp';
buvid = 'buvid';
} }
abstract class VideoBoxKey { abstract class VideoBoxKey {

View File

@@ -26,7 +26,6 @@ import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
import 'package:PiliPlus/utils/context_ext.dart'; import 'package:PiliPlus/utils/context_ext.dart';
import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/login_utils.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/storage_key.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -801,15 +800,6 @@ abstract class Pref {
static bool get silentDownImg => static bool get silentDownImg =>
_setting.get(SettingBoxKey.silentDownImg, defaultValue: false); _setting.get(SettingBoxKey.silentDownImg, defaultValue: false);
static String get buvid {
String? buvid = _localCache.get(LocalCacheKey.buvid);
if (buvid == null) {
buvid = LoginUtils.generateBuvid();
_localCache.put(LocalCacheKey.buvid, buvid);
}
return buvid;
}
static bool get showMemberShop => static bool get showMemberShop =>
_setting.get(SettingBoxKey.showMemberShop, defaultValue: false); _setting.get(SettingBoxKey.showMemberShop, defaultValue: false);