mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: account manager (#468)
* feat: account manager * remove dep * some fixes * migrate accounts * reimplement clearCookie
This commit is contained in:
committed by
GitHub
parent
94fa0652ac
commit
b15fdfa2ff
@@ -622,6 +622,8 @@ class Api {
|
||||
static const qrcodePoll =
|
||||
'${HttpString.passBaseUrl}/x/passport-tv-login/qrcode/poll';
|
||||
|
||||
static const logout = '${HttpString.passBaseUrl}/login/exit/v2';
|
||||
|
||||
/// 置顶视频
|
||||
static const getTopVideoApi = '/x/space/top/arc';
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
@@ -92,7 +93,7 @@ class DynamicsHttp {
|
||||
dynamic id,
|
||||
dynamic rid,
|
||||
dynamic type,
|
||||
bool? clearCookie,
|
||||
bool clearCookie = false,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.dynamicDetail,
|
||||
@@ -104,7 +105,7 @@ class DynamicsHttp {
|
||||
'features': 'itemOpusStyle',
|
||||
},
|
||||
options:
|
||||
clearCookie == true ? Options(extra: {'clearCookie': true}) : null,
|
||||
clearCookie ? Options(extra: {'account': AnonymousAccount()}) : null,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
try {
|
||||
|
||||
@@ -4,20 +4,17 @@ import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:math' show Random;
|
||||
import 'package:PiliPlus/build_config.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account_manager/account_mgr.dart';
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:brotli/brotli.dart';
|
||||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio/io.dart';
|
||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import '../utils/storage.dart';
|
||||
import '../utils/utils.dart';
|
||||
import 'api.dart';
|
||||
import 'constants.dart';
|
||||
import 'interceptor.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
|
||||
|
||||
class Request {
|
||||
@@ -25,101 +22,69 @@ class Request {
|
||||
static const brotilDecoder = BrotliDecoder();
|
||||
|
||||
static final Request _instance = Request._internal();
|
||||
static late CookieManager cookieManager;
|
||||
static late AccountManager accountManager;
|
||||
static late final Dio dio;
|
||||
factory Request() => _instance;
|
||||
late bool enableSystemProxy;
|
||||
late String systemProxyHost;
|
||||
late String systemProxyPort;
|
||||
static final _rand = Random();
|
||||
static final RegExp spmPrefixExp =
|
||||
RegExp(r'<meta name="spm_prefix" content="([^"]+?)">');
|
||||
|
||||
/// 设置cookie
|
||||
static setCookie() async {
|
||||
final String cookiePath = await Utils.getCookiePath();
|
||||
final PersistCookieJar cookieJar = PersistCookieJar(
|
||||
ignoreExpires: true,
|
||||
storage: FileStorage(cookiePath),
|
||||
);
|
||||
cookieManager = CookieManager(cookieJar);
|
||||
dio.interceptors.add(cookieManager);
|
||||
dio.interceptors.add(ApiInterceptor());
|
||||
final List<Cookie> cookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.baseUrl));
|
||||
for (Cookie item in cookies) {
|
||||
await web.CookieManager().setCookie(
|
||||
url: web.WebUri(item.domain ?? ''),
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
path: item.path ?? '',
|
||||
domain: item.domain,
|
||||
isSecure: item.secure,
|
||||
isHttpOnly: item.httpOnly,
|
||||
);
|
||||
}
|
||||
final userInfo = GStorage.userInfo.get('userInfoCache');
|
||||
if (userInfo?.mid != null) {
|
||||
final List<Cookie> tUrlCookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.tUrl));
|
||||
if (tUrlCookies.isEmpty) {
|
||||
try {
|
||||
await dio.head(HttpString.tUrl);
|
||||
} catch (e) {
|
||||
log("setCookie, ${e.toString()}");
|
||||
}
|
||||
}
|
||||
setOptionsHeaders(userInfo);
|
||||
}
|
||||
|
||||
try {
|
||||
await buvidActivate();
|
||||
} catch (e) {
|
||||
log("setCookie, ${e.toString()}");
|
||||
}
|
||||
|
||||
// final String cookieString = cookies
|
||||
// .map((Cookie cookie) => '${cookie.name}=${cookie.value}')
|
||||
// .join('; ');
|
||||
// dio.options.headers['cookie'] = cookieString;
|
||||
accountManager = AccountManager();
|
||||
dio.interceptors.add(accountManager);
|
||||
await Accounts.refresh();
|
||||
final List<Cookie> cookies = Accounts.main.cookieJar.toList();
|
||||
final webManager = web.CookieManager();
|
||||
await Future.wait(cookies.map((item) => webManager.setCookie(
|
||||
url: web.WebUri(item.domain ?? ''),
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
path: item.path ?? '',
|
||||
domain: item.domain,
|
||||
isSecure: item.secure,
|
||||
isHttpOnly: item.httpOnly,
|
||||
)));
|
||||
}
|
||||
|
||||
// 从cookie中获取 csrf token
|
||||
static Future<String> getCsrf() async {
|
||||
List<Cookie> cookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.apiBaseUrl));
|
||||
return cookies
|
||||
.firstWhere((e) => e.name == 'bili_jct', orElse: () => Cookie('', ''))
|
||||
.value;
|
||||
return Accounts.main.csrf;
|
||||
}
|
||||
|
||||
static setOptionsHeaders(userInfo) {
|
||||
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
||||
dio.options.headers['x-bili-aurora-eid'] =
|
||||
IdUtils.genAuroraEid(userInfo.mid);
|
||||
}
|
||||
static Future<void> buvidActive(Account account) async {
|
||||
// 这样线程不安全, 但仍按预期进行
|
||||
if (account.activited) return;
|
||||
account.activited = true;
|
||||
try {
|
||||
final html = await Request().get(Api.dynamicSpmPrefix,
|
||||
options: Options(extra: {'account': account}));
|
||||
final String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!;
|
||||
final String randPngEnd = base64.encode(
|
||||
List<int>.generate(32, (_) => _rand.nextInt(256)) +
|
||||
List<int>.filled(4, 0) +
|
||||
[73, 69, 78, 68] +
|
||||
List<int>.generate(4, (_) => _rand.nextInt(256)));
|
||||
|
||||
static Future buvidActivate() async {
|
||||
var html = await Request().get(Api.dynamicSpmPrefix);
|
||||
String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!;
|
||||
Random rand = Random();
|
||||
String randPngEnd = base64.encode(
|
||||
List<int>.generate(32, (_) => rand.nextInt(256)) +
|
||||
List<int>.filled(4, 0) +
|
||||
[73, 69, 78, 68] +
|
||||
List<int>.generate(4, (_) => rand.nextInt(256)));
|
||||
String jsonData = json.encode({
|
||||
'3064': 1,
|
||||
'39c8': '$spmPrefix.fp.risk',
|
||||
'3c43': {
|
||||
'adca': 'Linux',
|
||||
'bfe9': randPngEnd.substring(randPngEnd.length - 50),
|
||||
},
|
||||
});
|
||||
|
||||
String jsonData = json.encode({
|
||||
'3064': 1,
|
||||
'39c8': '$spmPrefix.fp.risk',
|
||||
'3c43': {
|
||||
'adca': 'Linux',
|
||||
'bfe9': randPngEnd.substring(randPngEnd.length - 50),
|
||||
},
|
||||
});
|
||||
|
||||
await Request().post(Api.activateBuvidApi,
|
||||
data: {'payload': jsonData},
|
||||
options: Options(contentType: Headers.jsonContentType));
|
||||
await Request().post(Api.activateBuvidApi,
|
||||
data: {'payload': jsonData},
|
||||
options: Options(contentType: Headers.jsonContentType));
|
||||
;
|
||||
} catch (e) {
|
||||
log("setCookie, $e");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -225,7 +190,7 @@ class Request {
|
||||
} on DioException catch (e) {
|
||||
Response errResponse = Response(
|
||||
data: {
|
||||
'message': await ApiInterceptor.dioError(e)
|
||||
'message': await AccountManager.dioError(e)
|
||||
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
|
||||
statusCode: -1,
|
||||
requestOptions: RequestOptions(),
|
||||
@@ -254,7 +219,7 @@ class Request {
|
||||
} on DioException catch (e) {
|
||||
Response errResponse = Response(
|
||||
data: {
|
||||
'message': await ApiInterceptor.dioError(e)
|
||||
'message': await AccountManager.dioError(e)
|
||||
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
|
||||
statusCode: -1,
|
||||
requestOptions: RequestOptions(),
|
||||
@@ -279,7 +244,7 @@ class Request {
|
||||
return response.data;
|
||||
} on DioException catch (e) {
|
||||
debugPrint('downloadFile error: $e');
|
||||
return Future.error(ApiInterceptor.dioError(e));
|
||||
return Future.error(AccountManager.dioError(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class ApiInterceptor extends Interceptor {
|
||||
static const List<String> anonymityList = [
|
||||
Api.videoUrl,
|
||||
Api.videoIntro,
|
||||
Api.relatedList,
|
||||
Api.replyList,
|
||||
Api.replyReplyList,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType,
|
||||
Api.heartBeat,
|
||||
Api.ab2c,
|
||||
Api.bangumiInfo,
|
||||
Api.liveRoomInfo,
|
||||
Api.onlineTotal,
|
||||
Api.dynamicDetail,
|
||||
Api.aiConclusion,
|
||||
Api.getSeasonDetailApi,
|
||||
Api.liveRoomDmToken,
|
||||
Api.liveRoomDmPrefetch,
|
||||
];
|
||||
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
void onRemoveCookie() {
|
||||
options.headers.remove('x-bili-mid');
|
||||
options.headers.remove('x-bili-aurora-eid');
|
||||
options.headers.remove('x-bili-aurora-zone');
|
||||
options.headers['cookie'] = '';
|
||||
options.queryParameters.remove('access_key');
|
||||
options.queryParameters.remove('csrf');
|
||||
options.queryParameters.remove('csrf_token');
|
||||
if (options.data is Map) {
|
||||
options.data.remove('access_key');
|
||||
options.data.remove('csrf');
|
||||
options.data.remove('csrf_token');
|
||||
}
|
||||
}
|
||||
|
||||
// app端不需要cookie
|
||||
// if (options.uri.host == 'app.bilibili.com') {
|
||||
// options.headers.remove('cookie');
|
||||
// }
|
||||
|
||||
if (options.extra['clearCookie'] == true) {
|
||||
onRemoveCookie();
|
||||
} else if (MineController.anonymity.value) {
|
||||
String uri = options.uri.toString();
|
||||
for (var i in anonymityList) {
|
||||
// 如果请求的url包含无痕列表中的url,则清空cookie
|
||||
// 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤
|
||||
int index = uri.indexOf(i);
|
||||
if (index == -1) continue;
|
||||
if (uri.lastIndexOf('/') >= index + i.length) continue;
|
||||
//SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}');
|
||||
onRemoveCookie();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler.next(options);
|
||||
}
|
||||
|
||||
// @override
|
||||
// void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
// try {
|
||||
// if (response.statusCode == 302) {
|
||||
// final List<String> locations = response.headers['location']!;
|
||||
// if (locations.isNotEmpty) {
|
||||
// if (locations.first.startsWith('https://www.mcbbs.net')) {
|
||||
// debugPrint('ApiInterceptor@@@@@: ${locations.first}');
|
||||
// final Uri uri = Uri.parse(locations.first);
|
||||
// final String? accessKey = uri.queryParameters['access_key'];
|
||||
// final String? mid = uri.queryParameters['mid'];
|
||||
// try {
|
||||
// GStorage.localCache.put(LocalCacheKey.accessKey,
|
||||
// <String, String?>{'mid': mid, 'value': accessKey});
|
||||
// } catch (_) {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (err) {
|
||||
// debugPrint('ApiInterceptor: $err');
|
||||
// }
|
||||
|
||||
// handler.next(response);
|
||||
// }
|
||||
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||
// 处理网络请求错误
|
||||
// handler.next(err);
|
||||
String url = err.requestOptions.uri.toString();
|
||||
debugPrint('🌹🌹ApiInterceptor: $url');
|
||||
if (url.contains('heartbeat') ||
|
||||
url.contains('seg.so') ||
|
||||
url.contains('online/total') ||
|
||||
url.contains('github') ||
|
||||
(url.contains('skipSegments') && err.requestOptions.method == 'GET')) {
|
||||
// skip
|
||||
} else {
|
||||
SmartDialog.showToast(
|
||||
await dioError(err) + url,
|
||||
// displayType: SmartToastType.onlyRefresh,
|
||||
// displayTime: const Duration(milliseconds: 1200),
|
||||
);
|
||||
}
|
||||
super.onError(err, handler);
|
||||
}
|
||||
|
||||
static Future<String> dioError(DioException error) async {
|
||||
switch (error.type) {
|
||||
case DioExceptionType.badCertificate:
|
||||
return '证书有误!';
|
||||
case DioExceptionType.badResponse:
|
||||
return '服务器异常,请稍后重试!';
|
||||
case DioExceptionType.cancel:
|
||||
return '请求已被取消,请重新请求';
|
||||
case DioExceptionType.connectionError:
|
||||
return '连接错误,请检查网络设置';
|
||||
case DioExceptionType.connectionTimeout:
|
||||
return '网络连接超时,请检查网络设置';
|
||||
case DioExceptionType.receiveTimeout:
|
||||
return '响应超时,请稍后重试!';
|
||||
case DioExceptionType.sendTimeout:
|
||||
return '发送请求超时,请检查网络设置';
|
||||
case DioExceptionType.unknown:
|
||||
final String res =
|
||||
(await Connectivity().checkConnectivity()).first.title;
|
||||
return '$res网络异常 ${error.error}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _ConnectivityResultExt on ConnectivityResult {
|
||||
String get title => const ['蓝牙', 'Wi-Fi', '局域', '流量', '无', '代理', '其他'][index];
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
@@ -11,9 +12,7 @@ import 'index.dart';
|
||||
class LoginHttp {
|
||||
static final String deviceId = LoginUtils.genDeviceId();
|
||||
static final String buvid = LoginUtils.buvid;
|
||||
static const String host = 'passport.bilibili.com';
|
||||
static final Map<String, String> headers = {
|
||||
'Host': host,
|
||||
'buvid': buvid,
|
||||
'env': 'prod',
|
||||
'app-key': 'android_hd',
|
||||
@@ -27,21 +26,14 @@ class LoginHttp {
|
||||
|
||||
static Future<Map<String, dynamic>> getHDcode() async {
|
||||
var params = {
|
||||
'appkey': Constants.appKey,
|
||||
// 'local_id': 'Y952A395BB157D305D8A8340FC2AAECECE17',
|
||||
'local_id': '0',
|
||||
//精确到秒的时间戳
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'platform': 'android',
|
||||
'mobi_app': 'android_hd',
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
params,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
var res = await Request()
|
||||
.post(Api.getTVCode, queryParameters: {...params, 'sign': sign});
|
||||
Utils.appSign(params);
|
||||
var res = await Request().post(Api.getTVCode, queryParameters: params);
|
||||
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
@@ -52,18 +44,12 @@ class LoginHttp {
|
||||
|
||||
static Future codePoll(String authCode) async {
|
||||
var params = {
|
||||
'appkey': Constants.appKey,
|
||||
'auth_code': authCode,
|
||||
'local_id': '0',
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
params,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
var res = await Request()
|
||||
.post(Api.qrcodePoll, queryParameters: {...params, 'sign': sign});
|
||||
Utils.appSign(params);
|
||||
var res = await Request().post(Api.qrcodePoll, queryParameters: params);
|
||||
return {
|
||||
'status': res.data['code'] == 0,
|
||||
'code': res.data['code'],
|
||||
@@ -106,7 +92,6 @@ class LoginHttp {
|
||||
}) async {
|
||||
int timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
var data = {
|
||||
'appkey': Constants.appKey,
|
||||
'build': '2001100',
|
||||
'buvid': buvid,
|
||||
'c_locale': 'zh_CN',
|
||||
@@ -129,15 +114,11 @@ class LoginHttp {
|
||||
'tel': tel,
|
||||
'ts': (timestamp ~/ 1000).toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
Utils.appSign(data);
|
||||
|
||||
var res = await Request().post(
|
||||
Api.appSmsCode,
|
||||
data: {...data, 'sign': sign},
|
||||
data: data,
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
headers: headers,
|
||||
@@ -211,7 +192,6 @@ class LoginHttp {
|
||||
Encrypter(RSA(publicKey: publicKey)).encrypt(salt + password).base64;
|
||||
|
||||
Map<String, String> data = {
|
||||
'appkey': Constants.appKey,
|
||||
'bili_local_id': deviceId,
|
||||
'build': '2001100',
|
||||
'buvid': buvid,
|
||||
@@ -242,15 +222,7 @@ class LoginHttp {
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'username': username,
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
data.map((key, value) {
|
||||
return MapEntry<String, dynamic>(key, value);
|
||||
});
|
||||
Utils.appSign(data);
|
||||
var res = await Request().post(
|
||||
Api.loginByPwdApi,
|
||||
data: data,
|
||||
@@ -287,7 +259,6 @@ class LoginHttp {
|
||||
}) async {
|
||||
dynamic publicKey = RSAKeyParser().parse(key);
|
||||
Map<String, String> data = {
|
||||
'appkey': Constants.appKey,
|
||||
'bili_local_id': deviceId,
|
||||
'build': '2001100',
|
||||
'buvid': buvid,
|
||||
@@ -316,15 +287,7 @@ class LoginHttp {
|
||||
'tel': tel,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
data.map((key, value) {
|
||||
return MapEntry<String, dynamic>(key, value);
|
||||
});
|
||||
Utils.appSign(data);
|
||||
var res = await Request().post(
|
||||
Api.logInByAppSms,
|
||||
data: data,
|
||||
@@ -404,12 +367,7 @@ class LoginHttp {
|
||||
if (geeValidate != null) 'gee_validate': geeValidate,
|
||||
if (recaptchaToken != null) 'recaptcha_token': recaptchaToken,
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
Utils.appSign(data);
|
||||
var res = await Request().post(
|
||||
Api.safeCenterSmsCode,
|
||||
data: data,
|
||||
@@ -449,12 +407,7 @@ class LoginHttp {
|
||||
'source': source,
|
||||
'captcha_key': captchaKey,
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
Utils.appSign(data);
|
||||
var res = await Request().post(
|
||||
Api.safeCenterSmsVerify,
|
||||
data: data,
|
||||
@@ -500,15 +453,7 @@ class LoginHttp {
|
||||
// 'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
data.map((key, value) {
|
||||
return MapEntry<String, dynamic>(key, value);
|
||||
});
|
||||
Utils.appSign(data);
|
||||
var res = await Request().post(
|
||||
Api.oauth2AccessToken,
|
||||
data: data,
|
||||
@@ -529,4 +474,15 @@ class LoginHttp {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map> logout(Account account) async {
|
||||
dynamic res = await Request().post(
|
||||
Api.logout,
|
||||
data: {'biliCSRF': account.csrf},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
extra: {'account': account}),
|
||||
);
|
||||
return {'status': res.data['code'] == 0, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,7 @@ class MemberHttp {
|
||||
required int mid,
|
||||
required int page,
|
||||
}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
Map<String, String> data = {
|
||||
if (accessKey?.isNotEmpty == true) 'access_key': accessKey!,
|
||||
'appkey': Constants.appKey,
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
@@ -79,15 +75,8 @@ class MemberHttp {
|
||||
'ps': '10',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'vmid': mid.toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
dynamic res = await Request().get(
|
||||
Api.spaceArticle,
|
||||
queryParameters: data,
|
||||
@@ -108,11 +97,7 @@ class MemberHttp {
|
||||
static Future<LoadingState> spaceFav({
|
||||
required int mid,
|
||||
}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
Map<String, String> data = {
|
||||
if (accessKey?.isNotEmpty == true) 'access_key': accessKey!,
|
||||
'appkey': Constants.appKey,
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
@@ -120,15 +105,8 @@ class MemberHttp {
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'up_mid': mid.toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
dynamic res = await Request().get(
|
||||
Api.spaceFav,
|
||||
queryParameters: data,
|
||||
@@ -176,12 +154,8 @@ class MemberHttp {
|
||||
int? seasonId,
|
||||
int? seriesId,
|
||||
}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
Map<String, String> data = {
|
||||
if (accessKey?.isNotEmpty == true) 'access_key': accessKey!,
|
||||
if (aid != null) 'aid': aid.toString(),
|
||||
'appkey': Constants.appKey,
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
@@ -197,25 +171,16 @@ class MemberHttp {
|
||||
if (order != null) 'order': order,
|
||||
if (sort != null) 'sort': sort,
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'vmid': mid.toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
dynamic res = await Request().get(
|
||||
type == ContributeType.video
|
||||
? Api.spaceArchive
|
||||
: type == ContributeType.charging
|
||||
? Api.spaceChargingArchive
|
||||
: type == ContributeType.season
|
||||
? Api.spaceSeason
|
||||
: type == ContributeType.series
|
||||
? Api.spaceSeries
|
||||
: Api.spaceBangumi,
|
||||
switch (type) {
|
||||
ContributeType.video => Api.spaceArchive,
|
||||
ContributeType.charging => Api.spaceChargingArchive,
|
||||
ContributeType.season => Api.spaceSeason,
|
||||
ContributeType.series => Api.spaceSeries,
|
||||
ContributeType.bangumi => Api.spaceBangumi,
|
||||
},
|
||||
queryParameters: data,
|
||||
options: Options(
|
||||
headers: {
|
||||
@@ -234,11 +199,7 @@ class MemberHttp {
|
||||
static Future<LoadingState> space({
|
||||
int? mid,
|
||||
}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
Map<String, String> data = {
|
||||
if (accessKey?.isNotEmpty == true) 'access_key': accessKey!,
|
||||
'appkey': Constants.appKey,
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
@@ -246,15 +207,8 @@ class MemberHttp {
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'vmid': mid.toString(),
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
dynamic res = await Request().get(
|
||||
Api.space,
|
||||
queryParameters: data,
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||
import 'package:PiliPlus/grpc/grpc_repo.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
@@ -13,7 +14,8 @@ import 'api.dart';
|
||||
import 'init.dart';
|
||||
|
||||
class ReplyHttp {
|
||||
static Options get _options => Options(extra: {'clearCookie': true});
|
||||
static Options get _options =>
|
||||
Options(extra: {'account': AnonymousAccount()});
|
||||
|
||||
static RegExp replyRegExp =
|
||||
RegExp(GStorage.banWordForReply, caseSensitive: false);
|
||||
|
||||
@@ -18,9 +18,7 @@ import '../models/video_detail_res.dart';
|
||||
import '../utils/id_utils.dart';
|
||||
import '../utils/recommend_filter.dart';
|
||||
import '../utils/storage.dart';
|
||||
import '../utils/utils.dart';
|
||||
import '../utils/wbi_sign.dart';
|
||||
import '../pages/mine/controller.dart';
|
||||
import 'api.dart';
|
||||
import 'init.dart';
|
||||
import 'login.dart';
|
||||
@@ -71,15 +69,8 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
// 添加额外的loginState变量模拟未登录状态
|
||||
static Future<LoadingState> rcmdVideoListApp(
|
||||
{bool loginStatus = true, required int freshIdx}) async {
|
||||
static Future<LoadingState> rcmdVideoListApp({required int freshIdx}) async {
|
||||
Map<String, String> data = {
|
||||
'access_key': loginStatus
|
||||
? (GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'] ??
|
||||
'')
|
||||
: '',
|
||||
'appkey': Constants.appKey,
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
@@ -106,16 +97,8 @@ class VideoHttp {
|
||||
's_locale': 'zh_CN',
|
||||
'splash_id': '',
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
'voice_balance': '0'
|
||||
};
|
||||
String sign = Utils.appSign(
|
||||
data,
|
||||
Constants.appKey,
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
|
||||
var res = await Request().get(
|
||||
Api.recommendListApp,
|
||||
queryParameters: data,
|
||||
@@ -227,24 +210,22 @@ class VideoHttp {
|
||||
'qn': qn ?? 80,
|
||||
// 获取所有格式的视频
|
||||
'fnval': 4048,
|
||||
};
|
||||
|
||||
// 免登录查看1080p
|
||||
if ((GStorage.userInfo.get('userInfoCache') == null ||
|
||||
MineController.anonymity.value) &&
|
||||
GStorage.setting.get(SettingBoxKey.p1080, defaultValue: true)) {
|
||||
data['try_look'] = 1;
|
||||
}
|
||||
|
||||
Map params = await WbiSign.makSign({
|
||||
...data,
|
||||
'fourk': 1,
|
||||
'voice_balance': 1,
|
||||
'gaia_source': 'pre-load',
|
||||
'web_location': 1550101,
|
||||
});
|
||||
};
|
||||
|
||||
late final usePgcApi = forcePgcApi == true || GStorage.isLogin;
|
||||
// 免登录查看1080p
|
||||
if ((Accounts.get(AccountType.video).isLogin) &&
|
||||
GStorage.setting.get(SettingBoxKey.p1080, defaultValue: true)) {
|
||||
data['try_look'] = 1;
|
||||
}
|
||||
|
||||
Map params = await WbiSign.makSign(data);
|
||||
|
||||
late final usePgcApi =
|
||||
forcePgcApi == true || Accounts.get(AccountType.video).isLogin;
|
||||
|
||||
try {
|
||||
var res = await Request().get(
|
||||
@@ -326,7 +307,7 @@ class VideoHttp {
|
||||
data: {
|
||||
'platform': 'web',
|
||||
'season_id': seasonId,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
@@ -427,14 +408,12 @@ class VideoHttp {
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.coinVideo,
|
||||
queryParameters: {
|
||||
'aid': IdUtils.bv2av(bvid),
|
||||
data: {
|
||||
'aid': IdUtils.bv2av(bvid).toString(),
|
||||
// 'bvid': bvid,
|
||||
'multiply': multiply,
|
||||
'select_like': selectLike,
|
||||
'access_key': GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'],
|
||||
// 'csrf': await Request.getCsrf(),
|
||||
'multiply': multiply.toString(),
|
||||
'select_like': selectLike.toString(),
|
||||
// 'csrf': Accounts.main.csrf,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
@@ -461,7 +440,7 @@ class VideoHttp {
|
||||
Api.triple,
|
||||
data: {
|
||||
'ep_id': epId,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
@@ -489,7 +468,7 @@ class VideoHttp {
|
||||
'ramval': 0,
|
||||
'source': 'web_normal',
|
||||
'ga': 1,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
@@ -509,18 +488,19 @@ class VideoHttp {
|
||||
|
||||
// (取消)点赞
|
||||
static Future likeVideo({required String bvid, required bool type}) async {
|
||||
var res = await Request().post(Api.likeVideo, queryParameters: {
|
||||
'aid': IdUtils.bv2av(bvid),
|
||||
'like': type ? 0 : 1,
|
||||
'access_key': GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'],
|
||||
}
|
||||
// queryParameters: {
|
||||
// 'bvid': bvid,
|
||||
// 'like': type ? 1 : 2,
|
||||
// 'csrf': await Request.getCsrf(),
|
||||
// },
|
||||
);
|
||||
var res = await Request().post(
|
||||
Api.likeVideo,
|
||||
data: {
|
||||
'aid': IdUtils.bv2av(bvid).toString(),
|
||||
'like': type ? '0' : '1',
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
// queryParameters: {
|
||||
// 'bvid': bvid,
|
||||
// 'like': type ? 1 : 2,
|
||||
// 'csrf': Accounts.main.csrf,
|
||||
// },
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
@@ -530,17 +510,14 @@ class VideoHttp {
|
||||
|
||||
// (取消)点踩
|
||||
static Future dislikeVideo({required String bvid, required bool type}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
if (accessKey == null || accessKey == "") {
|
||||
if (Accounts.main.accessKey.isNullOrEmpty) {
|
||||
return {'status': false, 'msg': "请退出账号后重新登录"};
|
||||
}
|
||||
var res = await Request().post(
|
||||
Api.dislikeVideo,
|
||||
queryParameters: {
|
||||
'aid': IdUtils.bv2av(bvid),
|
||||
'dislike': type ? 0 : 1,
|
||||
'access_key': accessKey,
|
||||
data: {
|
||||
'aid': IdUtils.bv2av(bvid).toString(),
|
||||
'dislike': type ? '0' : '1',
|
||||
},
|
||||
);
|
||||
if (res.data is! String && res.data['code'] == 0) {
|
||||
@@ -559,9 +536,7 @@ class VideoHttp {
|
||||
required int id,
|
||||
int? reasonId,
|
||||
int? feedbackId}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
if (accessKey == null || accessKey == "") {
|
||||
if (Accounts.get(AccountType.recommend).accessKey.isNullOrEmpty) {
|
||||
return {'status': false, 'msg': "请退出账号后重新登录"};
|
||||
}
|
||||
assert((reasonId != null) ^ (feedbackId != null));
|
||||
@@ -571,10 +546,8 @@ class VideoHttp {
|
||||
// 'mid': mid,
|
||||
if (reasonId != null) 'reason_id': reasonId,
|
||||
if (feedbackId != null) 'feedback_id': feedbackId,
|
||||
'build': 1,
|
||||
'build': '1',
|
||||
'mobi_app': 'android',
|
||||
'access_key': accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
@@ -589,9 +562,7 @@ class VideoHttp {
|
||||
required int id,
|
||||
int? reasonId,
|
||||
int? feedbackId}) async {
|
||||
String? accessKey = GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
|
||||
if (accessKey == null || accessKey == "") {
|
||||
if (Accounts.get(AccountType.recommend).accessKey.isNullOrEmpty) {
|
||||
return {'status': false, 'msg': "请退出账号后重新登录"};
|
||||
}
|
||||
// assert ((reasonId != null) ^ (feedbackId != null));
|
||||
@@ -601,10 +572,8 @@ class VideoHttp {
|
||||
// 'mid': mid,
|
||||
if (reasonId != null) 'reason_id': reasonId,
|
||||
if (feedbackId != null) 'feedback_id': feedbackId,
|
||||
'build': 1,
|
||||
'build': '1',
|
||||
'mobi_app': 'android',
|
||||
'access_key': accessKey,
|
||||
'appkey': Constants.appKey,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
@@ -624,7 +593,7 @@ class VideoHttp {
|
||||
'resources': ids?.join(','),
|
||||
'media_id': delIds,
|
||||
'platform': 'web',
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
@@ -649,7 +618,7 @@ class VideoHttp {
|
||||
'type': type ?? 2,
|
||||
'add_media_ids': addIds ?? '',
|
||||
'del_media_ids': delIds ?? '',
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
@@ -672,7 +641,7 @@ class VideoHttp {
|
||||
// 'resources': '$epId:24',
|
||||
// 'add_media_ids': addIds ?? '',
|
||||
// 'del_media_ids': delIds ?? '',
|
||||
// 'csrf': await Request.getCsrf(),
|
||||
// 'csrf': Accounts.main.csrf,
|
||||
// },
|
||||
// options: Options(
|
||||
// headers: {
|
||||
@@ -709,7 +678,7 @@ class VideoHttp {
|
||||
if (mid != null) 'mid': mid,
|
||||
'resources': resources.join(','),
|
||||
'platform': 'web',
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
@@ -783,7 +752,7 @@ class VideoHttp {
|
||||
'message': message,
|
||||
if (pictures != null) 'pictures': jsonEncode(pictures),
|
||||
if (syncToDynamic == true) 'sync_to_dynamic': 1,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
};
|
||||
var res = await Request().post(
|
||||
Api.replyAdd,
|
||||
@@ -806,7 +775,7 @@ class VideoHttp {
|
||||
'type': type, //type.index
|
||||
'oid': oid,
|
||||
'rpid': rpid,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
});
|
||||
log(res.toString());
|
||||
if (res.data['code'] == 0) {
|
||||
@@ -842,7 +811,7 @@ class VideoHttp {
|
||||
"entity_id": mid,
|
||||
'fp': Request.headerUa(type: 'pc'),
|
||||
},
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
@@ -878,7 +847,7 @@ class VideoHttp {
|
||||
if (epid != null) 'type': 4,
|
||||
if (subType != null) 'sub_type': subType,
|
||||
'played_time': progress,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -891,7 +860,7 @@ class VideoHttp {
|
||||
'desc': desc,
|
||||
'oid': oid,
|
||||
'upper_mid': upperMid,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -899,7 +868,7 @@ class VideoHttp {
|
||||
static Future bangumiAdd({int? seasonId}) async {
|
||||
var res = await Request().post(Api.bangumiAdd, queryParameters: {
|
||||
'season_id': seasonId,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
@@ -920,7 +889,7 @@ class VideoHttp {
|
||||
static Future bangumiDel({int? seasonId}) async {
|
||||
var res = await Request().post(Api.bangumiDel, queryParameters: {
|
||||
'season_id': seasonId,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
@@ -946,7 +915,7 @@ class VideoHttp {
|
||||
data: {
|
||||
'season_id': seasonId,
|
||||
'status': status,
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
@@ -1143,7 +1112,7 @@ class VideoHttp {
|
||||
var res = await Request().get(
|
||||
Api.noteList,
|
||||
queryParameters: {
|
||||
'csrf': await Request.getCsrf(),
|
||||
'csrf': Accounts.main.csrf,
|
||||
'oid': oid,
|
||||
'oid_type': 0,
|
||||
'pn': page,
|
||||
|
||||
Reference in New Issue
Block a user