diff --git a/lib/grpc/grpc_req.dart b/lib/grpc/grpc_req.dart index 2a0c6ef79..b801de4e7 100644 --- a/lib/grpc/grpc_req.dart +++ b/lib/grpc/grpc_req.dart @@ -13,7 +13,6 @@ import 'package:PiliPlus/http/init.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:dio/dio.dart'; @@ -21,35 +20,46 @@ import 'package:flutter/foundation.dart' show kDebugMode; import 'package:protobuf/protobuf.dart' show GeneratedMessage; class GrpcReq { - static String? _accessKey = Accounts.main.accessKey; 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 _traceId = IdUtils.genTraceId(); static final _sessionId = Utils.generateRandomString(8); - static void updateHeaders(String? accessKey) { - _accessKey = accessKey; - if (_accessKey != null) { - headers['authorization'] = 'identify_v1 $_accessKey'; + static void updateHeaders(String? accessKey, String buvid) { + if (accessKey != null) { + headers['authorization'] = 'identify_v1 $accessKey'; } else { headers.remove('authorization'); } headers['x-bili-metadata-bin'] = base64Encode( Metadata( - accessKey: _accessKey ?? '', + accessKey: accessKey ?? '', mobiApp: _mobiApp, device: _device, build: _build, channel: _biliChannel, - buvid: _buvid, + buvid: buvid, platform: _device, ).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); } @@ -61,8 +71,9 @@ class GrpcReq { 'x-bili-gaia-vtoken': '', 'x-bili-aurora-zone': '', 'x-bili-trace-id': _traceId, - if (_accessKey != null) 'authorization': 'identify_v1 $_accessKey', - 'buvid': _buvid, + if (Accounts.main.accessKey != null) + 'authorization': 'identify_v1 ${Accounts.main.accessKey}', + 'buvid': Accounts.main.buvid, 'bili-http-engine': 'cronet', 'te': 'trailers', 'x-bili-fawkes-req-bin': base64Encode( @@ -74,12 +85,12 @@ class GrpcReq { ), 'x-bili-metadata-bin': base64Encode( Metadata( - accessKey: _accessKey ?? '', + accessKey: Accounts.main.accessKey ?? '', mobiApp: _mobiApp, device: _device, build: _build, channel: _biliChannel, - buvid: _buvid, + buvid: Accounts.main.buvid, platform: _device, ).writeToBuffer(), ), @@ -87,7 +98,7 @@ class GrpcReq { Device( appId: 5, build: _build, - buvid: _buvid, + buvid: Accounts.main.buvid, mobiApp: _mobiApp, platform: _device, channel: _biliChannel, @@ -141,7 +152,7 @@ class GrpcReq { GeneratedMessage request, T Function(Uint8List) grpcParser, ) async { - final response = await Request().post( + final response = await Request().post( HttpString.appBaseUrl + url, data: compressProtobuf(request.writeToBuffer()), options: options, diff --git a/lib/http/live.dart b/lib/http/live.dart index 295978042..1fd2f369c 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -2,7 +2,6 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/http/login.dart'; import 'package:PiliPlus/http/ua_type.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/common/live_search_type.dart'; @@ -211,7 +210,7 @@ abstract final class LiveHttp { queryParameters: params, options: Options( headers: { - 'buvid': LoginHttp.buvid, + 'buvid': recommend.buvid, 'fp_local': '1111111111111111111111111111111111111111111111111111111111111111', 'fp_remote': @@ -297,7 +296,7 @@ abstract final class LiveHttp { queryParameters: params, options: Options( headers: { - 'buvid': LoginHttp.buvid, + 'buvid': recommend.buvid, 'fp_local': '1111111111111111111111111111111111111111111111111111111111111111', 'fp_remote': diff --git a/lib/http/login.dart b/lib/http/login.dart index 8b7ced525..5d30842dc 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -17,9 +17,9 @@ import 'package:encrypt/encrypt.dart'; class LoginHttp { static final String deviceId = LoginUtils.genDeviceId(); - static String get buvid => LoginUtils.buvid; + static String get _buvid => AnonymousAccount().buvid; static final Map headers = { - 'buvid': buvid, + 'buvid': _buvid, 'env': 'prod', 'app-key': 'android_hd', 'user-agent': Constants.userAgent, @@ -99,7 +99,7 @@ class LoginHttp { int timestamp = DateTime.now().millisecondsSinceEpoch; var data = { 'build': '2001100', - 'buvid': buvid, + 'buvid': _buvid, 'c_locale': 'zh_CN', 'channel': 'master', 'cid': cid, @@ -108,10 +108,10 @@ class LoginHttp { 'gee_challenge': ?geeChallenge, 'gee_seccode': ?geeSeccode, 'gee_validate': ?geeValidate, - 'local_id': buvid, + 'local_id': _buvid, // https://chinggg.github.io/post/appre/ 'login_session_id': md5 - .convert(utf8.encode(buvid + timestamp.toString())) + .convert(utf8.encode(_buvid + timestamp.toString())) .toString(), 'mobi_app': 'android_hd', 'platform': 'android', @@ -202,7 +202,7 @@ class LoginHttp { Map data = { 'bili_local_id': deviceId, 'build': '2001100', - 'buvid': buvid, + 'buvid': _buvid, 'c_locale': 'zh_CN', 'channel': 'master', 'device': 'phone', @@ -221,7 +221,7 @@ class LoginHttp { 'gee_challenge': ?geeChallenge, 'gee_seccode': ?geeSeccode, 'gee_validate': ?geeValidate, - 'local_id': buvid, //LoginUtils.generateBuvid(), + 'local_id': _buvid, //LoginUtils.generateBuvid(), 'mobi_app': 'android_hd', 'password': passwordEncrypted, 'permission': 'ALL', @@ -271,7 +271,7 @@ class LoginHttp { Map data = { 'bili_local_id': deviceId, 'build': '2001100', - 'buvid': buvid, + 'buvid': _buvid, 'c_locale': 'zh_CN', 'captcha_key': captchaKey, 'channel': 'master', @@ -291,7 +291,7 @@ class LoginHttp { ), 'from_pv': 'main.my-information.my-login.0.click', 'from_url': Uri.encodeComponent('bilibili://user_center/mine'), - 'local_id': buvid, + 'local_id': _buvid, 'mobi_app': 'android_hd', 'platform': 'android', 's_locale': 'zh_CN', @@ -452,7 +452,7 @@ class LoginHttp { Map data = { 'appkey': Constants.appKey, 'build': '2001100', - 'buvid': buvid, + 'buvid': _buvid, // 'c_locale': 'zh_CN', // 'channel': 'master', 'code': code, @@ -462,7 +462,7 @@ class LoginHttp { // 'device_platform': 'Android14vivo', 'disable_rcmd': '0', 'grant_type': 'authorization_code', - 'local_id': buvid, + 'local_id': _buvid, 'mobi_app': 'android_hd', 'platform': 'android', // 's_locale': 'zh_CN', @@ -505,7 +505,7 @@ class LoginHttp { static Future> loginDevices() async { final account = Accounts.main; - final buvid = LoginUtils.buvid; + final buvid = account.buvid; final params = { 'local_id': buvid, 'buvid': buvid, diff --git a/lib/http/video.dart b/lib/http/video.dart index 0d9f91c44..f82355e8f 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -4,7 +4,6 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/http/login.dart'; import 'package:PiliPlus/http/ua_type.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/common/video/video_type.dart'; @@ -112,7 +111,7 @@ class VideoHttp { queryParameters: params, options: Options( headers: { - 'buvid': LoginHttp.buvid, + 'buvid': Accounts.get(AccountType.recommend).buvid, 'fp_local': '1111111111111111111111111111111111111111111111111111111111111111', 'fp_remote': diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 7b61e1afd..944d134eb 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -688,6 +688,8 @@ class LoginPageController extends GetxController BiliCookieJar.fromList(cookieInfo), tokenInfo['access_token'], tokenInfo['refresh_token'], + {}, + AnonymousAccount().buvid, ); await Future.wait([account.onChange(), AnonymousAccount().delete()]); for (int i = 0; i < AccountType.values.length; i++) { diff --git a/lib/pages/setting/models/privacy_settings.dart b/lib/pages/setting/models/privacy_settings.dart index efab85103..6702aac2e 100644 --- a/lib/pages/setting/models/privacy_settings.dart +++ b/lib/pages/setting/models/privacy_settings.dart @@ -41,11 +41,9 @@ List get privacySettings => [ context: Get.context!, builder: (context) { return AlertDialog( - title: const Text('查看详情'), + title: const Text('账号模式详情'), content: SingleChildScrollView( - child: Text( - AccountManager.apiTypeSet[AccountType.heartbeat]!.join('\n'), - ), + child: _getAccountDetail(context), ), actions: [ TextButton( @@ -58,7 +56,26 @@ List get privacySettings => [ ); }, leading: const Icon(Icons.flag_outlined), - title: '了解无痕模式', - subtitle: '查看无痕模式作用的API列表', + title: '了解账号模式', + subtitle: '查看各个账号模式作用的API列表', ), ]; + +Widget _getAccountDetail(BuildContext context) { + final slivers = []; + 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, + ); +} diff --git a/lib/utils/accounts/account.dart b/lib/utils/accounts/account.dart index bd952313e..eb2e006eb 100644 --- a/lib/utils/accounts/account.dart +++ b/lib/utils/accounts/account.dart @@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/id_utils.dart'; +import 'package:PiliPlus/utils/login_utils.dart'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:hive/hive.dart'; @@ -32,6 +33,8 @@ sealed class Account { String? get refresh => throw UnimplementedError(); + String get buvid => throw UnimplementedError(); + const Account(); } @@ -51,6 +54,9 @@ class LoginAccount extends Account { @override @HiveField(3) final Set type; + @override + @HiveField(4) + final String buvid; @override bool activited = false; @@ -69,11 +75,19 @@ class LoginAccount extends Account { late final String csrf = cookieJar.domainCookies['bilibili.com']!['/']!['bili_jct']!.cookie.value; - @override - Future delete() => _box.delete(_midStr); + bool _hasDelete = false; @override - Future onChange() => _box.put(_midStr, this); + Future delete() { + assert(_hasDelete = true); + return _box.delete(_midStr); + } + + @override + Future onChange() { + assert(!_hasDelete); + return _box.put(_midStr, this); + } @override Map? toJson() => { @@ -81,6 +95,7 @@ class LoginAccount extends Account { 'accessKey': accessKey, 'refresh': refresh, 'type': type.map((i) => i.index).toList(), + 'buvid': buvid, }; late final String _midStr = cookieJar @@ -95,7 +110,9 @@ class LoginAccount extends Account { this.accessKey, this.refresh, [ Set? type, - ]) : type = type ?? {} { + String? buvid, + ]) : type = type ?? {}, + buvid = buvid ?? LoginUtils.generateBuvid() { cookieJar.setBuvid3(); } @@ -104,6 +121,7 @@ class LoginAccount extends Account { json['accessKey'], json['refresh'], (json['type'] as Iterable?)?.map((i) => AccountType.values[i]).toSet(), + json['buvid'], ); @override @@ -131,12 +149,15 @@ class AnonymousAccount extends Account { final String csrf = ''; @override final Map headers = Constants.baseHeaders; + @override + String buvid = LoginUtils.generateBuvid(); @override bool activited = false; @override Future delete() async { + buvid = LoginUtils.generateBuvid(); await cookieJar.deleteAll(); cookieJar.setBuvid3(); } diff --git a/lib/utils/accounts/account_adapter.dart b/lib/utils/accounts/account_adapter.dart index 2f4873374..4e9158ecc 100644 --- a/lib/utils/accounts/account_adapter.dart +++ b/lib/utils/accounts/account_adapter.dart @@ -18,13 +18,14 @@ class LoginAccountAdapter extends TypeAdapter { fields[1] as String?, fields[2] as String?, (fields[3] as List?)?.cast().toSet(), + fields[4] as String?, ); } @override void write(BinaryWriter writer, LoginAccount obj) { writer - ..writeByte(4) + ..writeByte(5) ..writeByte(0) ..write(obj.cookieJar) ..writeByte(1) @@ -32,7 +33,9 @@ class LoginAccountAdapter extends TypeAdapter { ..writeByte(2) ..write(obj.refresh) ..writeByte(3) - ..write(obj.type.toList()); + ..write(obj.type.toList()) + ..writeByte(4) + ..write(obj.buvid); } @override diff --git a/lib/utils/login_utils.dart b/lib/utils/login_utils.dart index 153c01e30..1848289ca 100644 --- a/lib/utils/login_utils.dart +++ b/lib/utils/login_utils.dart @@ -49,11 +49,12 @@ abstract class LoginUtils { static Future onLoginMain() async { final account = Accounts.main; - GrpcReq.updateHeaders(account.accessKey); - setWebCookie(account); - RequestUtils.syncHistoryStatus(); final result = await UserHttp.userInfo(); if (result.isSuccess) { + GrpcReq.updateHeaders(account.accessKey, account.buvid); + setWebCookie(account); + RequestUtils.syncHistoryStatus(); + final UserInfoData data = result.data; if (data.isLogin == true) { Get.find() @@ -114,7 +115,7 @@ abstract class LoginUtils { ..face.value = '' ..isLogin.value = false; - GrpcReq.updateHeaders(null); + GrpcReq.updateHeaders(null, AnonymousAccount().buvid); await Future.wait([ if (!Platform.isWindows) web.CookieManager.instance().deleteAllCookies(), @@ -166,8 +167,6 @@ abstract class LoginUtils { return 'XY${md5Str[2]}${md5Str[12]}${md5Str[22]}$md5Str'; } - static final buvid = Pref.buvid; - // static String getUUID() { // return const Uuid().v4().replaceAll('-', ''); // } diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart index 7f81b4047..4b07ba9a4 100644 --- a/lib/utils/storage_key.dart +++ b/lib/utils/storage_key.dart @@ -221,8 +221,7 @@ abstract class LocalCacheKey { blackMids = 'blackMids', danmakuFilterRules = 'danmakuFilterRules', mixinKey = 'mixinKey', - timeStamp = 'timeStamp', - buvid = 'buvid'; + timeStamp = 'timeStamp'; } abstract class VideoBoxKey { diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index 8d3e39448..ee3319d5c 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -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/extension.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_key.dart'; import 'package:flutter/material.dart'; @@ -801,15 +800,6 @@ abstract class Pref { static bool get silentDownImg => _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 => _setting.get(SettingBoxKey.showMemberShop, defaultValue: false);