feat: load config from text (#1772)

* feat: load config from text

* opt: login utils

* update

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>

---------

Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
My-Responsitories
2025-12-09 22:09:57 +08:00
committed by GitHub
parent b4daf5fbd8
commit 244ef22f54
20 changed files with 264 additions and 208 deletions

View File

@@ -367,15 +367,15 @@ class FavHttp {
}
static Future<LoadingState<FavPgcData>> favPgc({
required dynamic mid,
required int type,
required int pn,
int? followStatus,
Object? mid,
}) async {
var res = await Request().get(
Api.favPgc,
queryParameters: {
'vmid': mid,
'vmid': mid ?? Accounts.main.mid,
'type': type,
'follow_status': ?followStatus,
'pn': pn,

View File

@@ -61,14 +61,20 @@ class Request {
// 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, (_) => Utils.random.nextInt(256)) +
List<int>.filled(4, 0) +
[73, 69, 78, 68] +
List<int>.generate(4, (_) => Utils.random.nextInt(256)),
);
final String randPngEnd = base64.encode([
...Iterable<int>.generate(32, (_) => Utils.random.nextInt(256)),
0,
0,
0,
0,
73,
69,
78,
68,
...Iterable<int>.generate(4, (_) => Utils.random.nextInt(256)),
]);
String jsonData = json.encode({
final jsonData = json.encode({
'3064': 1,
'39c8': '333.1387.fp.risk',
'3c43': {

View File

@@ -4,22 +4,22 @@ part 'stat.g.dart';
@HiveType(typeId: 1)
class UserStat {
UserStat({
const UserStat({
this.following,
this.follower,
this.dynamicCount,
});
@HiveField(0)
int? following;
final int? following;
@HiveField(1)
int? follower;
final int? follower;
@HiveField(2)
int? dynamicCount;
final int? dynamicCount;
UserStat.fromJson(Map<String, dynamic> json) {
following = json['following'];
follower = json['follower'];
dynamicCount = json['dynamic_count'];
}
factory UserStat.fromJson(Map<String, dynamic> json) => UserStat(
following: json['following'],
follower: json['follower'],
dynamicCount: json['dynamic_count'],
);
}

View File

@@ -434,6 +434,78 @@ Future<void> showImportExportDialog<T>(
);
},
),
ListTile(
dense: true,
title: Text('输入$title', style: style),
onTap: () {
Get.back();
final key = GlobalKey<FormFieldState<String>>();
late T json;
String? forceErrorText;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('输入$title'),
constraints: const BoxConstraints(
minWidth: 420,
maxWidth: 420,
),
content: TextFormField(
key: key,
minLines: 4,
maxLines: 12,
autofocus: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
errorMaxLines: 3,
),
validator: (value) {
if (forceErrorText != null) return forceErrorText;
try {
json = jsonDecode(value!) as T;
return null;
} catch (e) {
if (e is FormatException) {}
return '解析json失败$e';
}
},
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
),
TextButton(
onPressed: () async {
if (key.currentState?.validate() == true) {
try {
if (await fromJson(json)) {
Get.back();
SmartDialog.showToast('导入成功');
return;
}
} catch (e) {
forceErrorText = '导入失败:$e';
}
key.currentState?.validate();
forceErrorText = null;
}
},
child: const Text('确定'),
),
],
);
},
);
},
),
],
);
},

View File

@@ -9,6 +9,7 @@ import 'package:PiliPlus/models_new/follow/data.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:easy_debounce/easy_throttle.dart';
@@ -16,7 +17,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
class DynamicsController extends GetxController
with GetSingleTickerProviderStateMixin, ScrollOrRefreshMixin {
with GetSingleTickerProviderStateMixin, ScrollOrRefreshMixin, AccountMixin {
@override
final ScrollController scrollController = ScrollController();
late final TabController tabController = TabController(
@@ -40,7 +41,8 @@ class DynamicsController extends GetxController
final upPanelPosition = Pref.upPanelPosition;
AccountService accountService = Get.find<AccountService>();
@override
final AccountService accountService = Get.find<AccountService>();
DynamicsTabController? get controller {
try {
@@ -95,7 +97,7 @@ class DynamicsController extends GetxController
isQuerying = true;
final res = await FollowHttp.followings(
vmid: accountService.mid,
vmid: Accounts.main.mid,
pn: _upPage,
orderType: 'attention',
ps: 50,
@@ -136,7 +138,7 @@ class DynamicsController extends GetxController
DynamicsHttp.followUp(),
if (_showAllUp)
FollowHttp.followings(
vmid: accountService.mid,
vmid: Accounts.main.mid,
pn: _upPage,
orderType: 'attention',
ps: 50,
@@ -188,13 +190,13 @@ class DynamicsController extends GetxController
}
@override
Future<void> onRefresh() async {
Future<void> onRefresh() {
if (_showAllUp) {
_upPage = 1;
_cacheUpList = null;
}
queryFollowUp();
await controller?.onRefresh();
return controller!.onRefresh();
}
@override
@@ -231,4 +233,7 @@ class DynamicsController extends GetxController
scrollController.dispose();
super.onClose();
}
@override
void onChangeAccount(bool isLogin) => onRefresh();
}

View File

@@ -5,6 +5,7 @@ import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/dynamics/up.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/live_follow/view.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -112,7 +113,7 @@ class _UpPanelState extends State<UpPanel> {
UpItem(
uname: '',
face: accountService.face.value,
mid: accountService.mid,
mid: Accounts.main.mid,
),
),
),

View File

@@ -6,12 +6,14 @@ import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class DynamicsTabController
extends CommonListController<DynamicsDataModel, DynamicItemModel> {
extends CommonListController<DynamicsDataModel, DynamicItemModel>
with AccountMixin {
DynamicsTabController({required this.dynamicsType});
final DynamicsTabType dynamicsType;
String offset = '';
@@ -87,4 +89,7 @@ class DynamicsTabController
loadingState.refresh();
} catch (_) {}
}
@override
void onChangeAccount(bool isLogin) => onRefresh();
}

View File

@@ -4,7 +4,6 @@ import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models_new/fav/fav_pgc/data.dart';
import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -39,7 +38,6 @@ class FavPgcController
@override
Future<LoadingState<FavPgcData>> customGetData() => FavHttp.favPgc(
mid: Accounts.main.mid,
type: type,
followStatus: followStatus,
pn: page,

View File

@@ -7,9 +7,10 @@ import 'package:PiliPlus/models_new/live/live_feed_index/data.dart';
import 'package:PiliPlus/models_new/live/live_second_list/data.dart';
import 'package:PiliPlus/models_new/live/live_second_list/tag.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:get/get.dart';
class LiveController extends CommonListController {
class LiveController extends CommonListController with AccountMixin {
@override
void onInit() {
super.onInit();
@@ -140,4 +141,7 @@ class LiveController extends CommonListController {
isEnd = false;
queryData();
}
@override
void onChangeAccount(bool isLogin) => onRefresh();
}

View File

@@ -19,22 +19,24 @@ import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/update.dart';
import 'package:collection/collection.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MainController extends GetxController
with GetSingleTickerProviderStateMixin {
AccountService accountService = Get.find<AccountService>();
with GetSingleTickerProviderStateMixin, AccountMixin {
@override
final AccountService accountService = Get.find<AccountService>();
List<NavigationBarType> navigationBars = <NavigationBarType>[];
StreamController<bool>? bottomBarStream;
late bool hideTabBar = Pref.hideTabBar;
late dynamic controller;
RxInt selectedIndex = 0.obs;
final RxInt selectedIndex = 0.obs;
RxInt dynCount = 0.obs;
final RxInt dynCount = 0.obs;
late DynamicBadgeMode dynamicBadgeMode;
late bool checkDynamic = Pref.checkDynamic;
late int dynamicPeriod = Pref.dynamicPeriod * 60 * 1000;
@@ -165,7 +167,7 @@ class MainController extends GetxController
var res = await Future.wait([_msgUnread(), _msgFeedUnread()]);
int count = res.fold(0, (prev, e) => prev + e);
final count = res.sum;
final countStr = count == 0
? ''
@@ -332,4 +334,13 @@ class MainController extends GetxController
controller.dispose();
super.onClose();
}
@override
void onChangeAccount(bool isLogin) {
if (isLogin) {
getUnreadDynamic();
} else {
setDynCount();
}
}
}

View File

@@ -89,9 +89,7 @@ class _EditProfilePageState extends State<EditProfilePage> {
res.data['data'],
);
_loadingState = Success(data);
accountService
..name.value = data.name!
..face.value = data.face!;
accountService.face.value = data.face!;
try {
UserInfoData userInfo = Pref.userInfoCache!
..uname = data.name
@@ -388,7 +386,6 @@ class _EditProfilePageState extends State<EditProfilePage> {
data
..name = _textController.text
..coins = data.coins! - 6;
accountService.name.value = _textController.text;
try {
UserInfoData userInfo = Pref.userInfoCache!
..uname = _textController.text;

View File

@@ -19,16 +19,17 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class MineController
extends CommonDataController<FavFolderData, FavFolderData> {
class MineController extends CommonDataController<FavFolderData, FavFolderData>
with AccountMixin {
@override
AccountService accountService = Get.find<AccountService>();
int? favFolderCount;
// 用户信息 头像、昵称、lv
Rx<UserInfoData> userInfo = UserInfoData().obs;
final Rx<UserInfoData> userInfo = UserInfoData().obs;
// 用户状态 动态、关注、粉丝
Rx<UserStat> userStat = UserStat().obs;
final Rx<UserStat> userStat = const UserStat().obs;
Rx<ThemeType> themeType = Pref.themeType.obs;
@@ -107,8 +108,6 @@ class MineController
GStorage.userInfo.put('userInfoCache', data);
}
accountService
..mid = data.mid!
..name.value = data.uname!
..face.value = data.face!
..isLogin.value = true;
} else {
@@ -145,7 +144,7 @@ class MineController
return FavHttp.userfavFolder(
pn: 1,
ps: 20,
mid: accountService.mid,
mid: Accounts.main.mid,
);
}
@@ -293,4 +292,15 @@ class MineController
queryUserInfo();
return super.onRefresh();
}
@override
void onChangeAccount(bool isLogin) {
if (isLogin) {
onRefresh();
} else {
userInfo.value = UserInfoData();
userStat.value = const UserStat();
loadingState.value = LoadingState.loading();
}
}
}

View File

@@ -14,14 +14,16 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
class PgcController
extends CommonListController<List<PgcIndexItem>?, PgcIndexItem> {
extends CommonListController<List<PgcIndexItem>?, PgcIndexItem>
with AccountMixin {
PgcController({required this.tabType});
final HomeTabType tabType;
late final showPgcTimeline =
tabType == HomeTabType.bangumi && Pref.showPgcTimeline;
AccountService accountService = Get.find<AccountService>();
@override
final accountService = Get.find<AccountService>();
@override
void onInit() {
@@ -92,7 +94,6 @@ class PgcController
}
followLoading = true;
var res = await FavHttp.favPgc(
mid: accountService.mid,
type: tabType == HomeTabType.bangumi ? 1 : 2,
pn: followPage,
);
@@ -142,4 +143,15 @@ class PgcController
followController?.dispose();
super.onClose();
}
@override
void onChangeAccount(bool isLogin) {
if (isLogin) {
followPage = 1;
followEnd = false;
queryPgcFollow();
} else {
followState.value = LoadingState.loading();
}
}
}

View File

@@ -14,7 +14,6 @@ import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart' hide ContextExtensionss;
class SearchPage extends StatefulWidget {
@@ -397,15 +396,10 @@ class _SearchPageState extends State<SearchPage> {
title: '历史记录',
toJson: () => jsonEncode(_searchController.historyList),
fromJson: (json) {
try {
final list = List<String>.from(json);
_searchController.historyList.value = list;
GStorage.historyWord.put('cacheList', list);
return true;
} catch (e) {
SmartDialog.showToast(e.toString());
return false;
}
final list = List<String>.from(json);
_searchController.historyList.value = list;
GStorage.historyWord.put('cacheList', list);
return true;
},
),
),

View File

@@ -151,30 +151,29 @@ class _SponsorBlockPageState extends State<SponsorBlockPage> {
title: Text('用户ID', style: titleStyle),
subtitle: Text(_userId, style: subTitleStyle),
onTap: () {
final key = GlobalKey<FormState>();
final key = GlobalKey<FormFieldState<String>>();
_textController.text = _userId;
showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Text('用户ID', style: titleStyle),
content: Form(
content: TextFormField(
key: key,
child: TextFormField(
minLines: 1,
maxLines: 4,
autofocus: true,
controller: _textController,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\d]+')),
],
validator: (value) {
if ((value?.length ?? -1) < 30) {
return '用户ID要求至少为30个字符长度的纯字符串';
}
return null;
},
),
minLines: 1,
maxLines: 4,
autofocus: true,
controller: _textController,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\d]+')),
],
decoration: const InputDecoration(errorMaxLines: 2),
validator: (value) {
if ((value?.length ?? -1) < 30) {
return '用户ID要求至少为30个字符长度的纯字符串';
}
return null;
},
),
actions: [
TextButton(

View File

@@ -57,6 +57,7 @@ import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:collection/collection.dart';
import 'package:dio/dio.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:fl_chart/fl_chart.dart';
@@ -2132,9 +2133,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
Future<void> screenshotWebp() async {
final videoInfo = videoDetailController.data;
final ids = videoInfo.dash!.video!.map((i) => i.id!).toSet();
final video = videoDetailController.findVideoByQa(
ids.reduce((p, n) => p < n ? p : n),
);
final video = videoDetailController.findVideoByQa(ids.min);
VideoQuality qa = video.quality;
String? url = video.baseUrl;
@@ -2513,7 +2512,7 @@ Widget buildDmChart(
minX: 0,
maxX: (dmTrend.length - 1).toDouble(),
minY: 0,
maxY: dmTrend.reduce((a, b) => a > b ? a : b).toDouble(),
maxY: dmTrend.max,
lineBarsData: [
LineChartBarData(
spots: List.generate(

View File

@@ -1,20 +1,44 @@
import 'dart:async';
import 'package:PiliPlus/models/user/info.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:get/get.dart';
class AccountService extends GetxService {
late int mid;
late final RxString name;
late final RxString face;
late final RxBool isLogin;
final RxString face = ''.obs;
final RxBool isLogin = false.obs;
@override
void onInit() {
super.onInit();
UserInfoData? userInfo = Pref.userInfoCache;
mid = userInfo?.mid ?? 0;
name = (userInfo?.uname ?? '').obs;
face = (userInfo?.face ?? '').obs;
isLogin = (userInfo != null).obs;
if (userInfo != null) {
face.value = userInfo.face ?? '';
isLogin.value = true;
} else {
face.value = '';
isLogin.value = false;
}
}
}
mixin AccountMixin on GetLifeCycleBase {
StreamSubscription<bool>? _listener;
AccountService get accountService => Get.find<AccountService>();
void onChangeAccount(bool isLogin);
@override
void onInit() {
super.onInit();
_listener = accountService.isLogin.listen(onChangeAccount);
}
@override
void onClose() {
_listener?.cancel();
_listener = null;
super.onClose();
}
}

View File

@@ -106,9 +106,9 @@ abstract class Accounts {
}
static Future<void> set(AccountType key, Account account) async {
await (accountMode[key.index]..type.remove(key)).onChange();
final oldAccount = accountMode[key.index]..type.remove(key);
accountMode[key.index] = account..type.add(key);
await account.onChange();
await Future.wait([account.onChange(), oldAccount.onChange()]);
if (!account.activated) await Request.buvidActive(account);
switch (key) {
case AccountType.main:

View File

@@ -2,19 +2,9 @@ import 'dart:async' show FutureOr;
import 'dart:io' show Platform;
import 'package:PiliPlus/grpc/grpc_req.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/main.dart';
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
import 'package:PiliPlus/models/common/home_tab_type.dart';
import 'package:PiliPlus/models/user/info.dart';
import 'package:PiliPlus/models/user/stat.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
import 'package:PiliPlus/pages/live/controller.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/pages/pgc/controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
@@ -22,11 +12,13 @@ import 'package:PiliPlus/utils/request_utils.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart' show Digest;
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
abstract class LoginUtils {
abstract final class LoginUtils {
static FutureOr setWebCookie([Account? account]) {
if (Platform.isLinux) {
return null;
@@ -62,48 +54,19 @@ abstract class LoginUtils {
if (result.isSuccess) {
final UserInfoData data = result.data;
if (data.isLogin == true) {
Get.find<AccountService>()
..mid = data.mid!
..name.value = data.uname!
..face.value = data.face!
..isLogin.value = true;
final accountService = Get.find<AccountService>()
..face.value = data.face!;
if (accountService.isLogin.value) {
accountService.isLogin.refresh();
} else {
accountService.isLogin.value = true;
}
SmartDialog.showToast('main登录成功');
if (data != Pref.userInfoCache) {
await GStorage.userInfo.put('userInfoCache', data);
}
try {
Get.find<MineController>().onRefresh();
} catch (_) {}
try {
Get.find<DynamicsController>().onRefresh();
} catch (_) {}
for (var item in DynamicsTabType.values) {
try {
Get.find<DynamicsTabController>(tag: item.name).onRefresh();
} catch (_) {}
}
try {
Get.find<LiveController>().onRefresh();
} catch (_) {}
try {
Get.find<PgcController>(tag: HomeTabType.bangumi.name)
..followPage = 1
..followEnd = false
..queryPgcFollow();
} catch (_) {}
try {
Get.find<PgcController>(tag: HomeTabType.cinema.name)
..followPage = 1
..followEnd = false
..queryPgcFollow();
} catch (_) {}
}
} else {
// 获取用户信息失败
@@ -115,65 +78,26 @@ abstract class LoginUtils {
}
}
static Future<void> onLogoutMain() async {
static Future<void> onLogoutMain() {
Get.find<AccountService>()
..mid = 0
..name.value = ''
..face.value = ''
..isLogin.value = false;
GrpcReq.updateHeaders(null);
await Future.wait([
return Future.wait([
if (!Platform.isLinux)
web.CookieManager.instance(
webViewEnvironment: webViewEnvironment,
).deleteAllCookies(),
GStorage.userInfo.delete('userInfoCache'),
]);
try {
Get.find<MainController>().setDynCount();
} catch (_) {}
try {
Get.find<MineController>()
..userInfo.value = UserInfoData()
..userStat.value = UserStat()
..loadingState.value = LoadingState.loading();
// MineController.anonymity.value = false;
} catch (_) {}
try {
Get.find<DynamicsController>().onRefresh();
} catch (_) {}
try {
Get.find<LiveController>().onRefresh();
} catch (_) {}
for (var item in DynamicsTabType.values) {
try {
Get.find<DynamicsTabController>(tag: item.name).onRefresh();
} catch (_) {}
}
try {
Get.find<PgcController>(tag: HomeTabType.bangumi.name).followState.value =
LoadingState.loading();
} catch (_) {}
try {
Get.find<PgcController>(tag: HomeTabType.cinema.name).followState.value =
LoadingState.loading();
} catch (_) {}
}
static String generateBuvid() {
var md5Str = Iterable.generate(
32,
(_) => Utils.random.nextInt(16).toRadixString(16),
).join().toUpperCase();
var md5Str = Digest(
List.generate(16, (_) => Utils.random.nextInt(256)),
).toString();
return 'XY${md5Str[2]}${md5Str[12]}${md5Str[22]}$md5Str';
}
@@ -190,31 +114,26 @@ abstract class LoginUtils {
static String genDeviceId() {
// https://github.com/bilive/bilive_client/blob/2873de0532c54832f5464a4c57325ad9af8b8698/bilive/lib/app_client.ts#L62
final String yyyyMMddHHmmss = DateTime.now()
.toIso8601String()
.replaceAll(RegExp(r'[-:TZ]'), '')
.substring(0, 14);
final time = DateTime.now();
final String randomHex32 = List.generate(
32,
(index) => Utils.random.nextInt(16).toRadixString(16),
).join();
final String randomHex16 = List.generate(
16,
(index) => Utils.random.nextInt(16).toRadixString(16),
).join();
final List<int> bytes = [
...Iterable.generate(16, (_) => Utils.random.nextInt(256)),
_dec2bcd(time.year ~/ 100),
_dec2bcd(time.year % 100),
_dec2bcd(time.month),
_dec2bcd(time.day),
_dec2bcd(time.hour),
_dec2bcd(time.minute),
_dec2bcd(time.second),
...Iterable.generate(8, (_) => Utils.random.nextInt(256)),
];
final check = (bytes.sum & 0xFF).toRadixString(16).padLeft(2, '0');
final String deviceID = randomHex32 + yyyyMMddHHmmss + randomHex16;
return Digest(bytes).toString() + check;
}
final List<int> bytes = RegExp(r'\w{2}')
.allMatches(deviceID)
.map((match) => int.parse(match.group(0)!, radix: 16))
.toList();
final int checksumValue = bytes.reduce((a, b) => a + b);
final String check = checksumValue
.toRadixString(16)
.substring(checksumValue.toRadixString(16).length - 2);
return deviceID + check;
static int _dec2bcd(int dec) {
assert(0 <= dec && dec < 100);
return ((dec ~/ 10) << 4) | (dec % 10);
}
}

View File

@@ -72,10 +72,10 @@ abstract class GStorage {
importAllJsonSettings(jsonDecode(data));
static Future<bool> importAllJsonSettings(Map<String, dynamic> map) async {
await setting.clear();
await video.clear();
await setting.putAll(map[setting.name]);
await video.putAll(map[video.name]);
await Future.wait([
setting.clear().then((_) => setting.putAll(map[setting.name])),
video.clear().then((_) => video.putAll(map[video.name])),
]);
return true;
}