189 lines
4.9 KiB
Dart
189 lines
4.9 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:fl_clash/clash/core.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:json_annotation/json_annotation.dart';
|
|
|
|
part 'generated/profile.g.dart';
|
|
|
|
typedef SelectedMap = Map<String, String>;
|
|
|
|
@JsonSerializable()
|
|
class UserInfo {
|
|
int upload;
|
|
int download;
|
|
int total;
|
|
int? expire;
|
|
|
|
UserInfo({
|
|
int? upload,
|
|
int? download,
|
|
int? total,
|
|
this.expire,
|
|
}) : upload = upload ?? 0,
|
|
download = download ?? 0,
|
|
total = total ?? 0;
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return _$UserInfoToJson(this);
|
|
}
|
|
|
|
factory UserInfo.fromJson(Map<String, dynamic> json) {
|
|
return _$UserInfoFromJson(json);
|
|
}
|
|
|
|
factory UserInfo.formHString(String? info) {
|
|
if (info == null) return UserInfo();
|
|
var list = info.split(";");
|
|
Map<String, int?> map = {};
|
|
for (var i in list) {
|
|
var keyValue = i.trim().split("=");
|
|
map[keyValue[0]] = int.tryParse(keyValue[1]);
|
|
}
|
|
return UserInfo(
|
|
upload: map["upload"],
|
|
download: map["download"],
|
|
total: map["total"],
|
|
expire: map["expire"],
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'UserInfo{upload: $upload, download: $download, total: $total, expire: $expire}';
|
|
}
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class Profile {
|
|
String id;
|
|
String? label;
|
|
String? proxyName;
|
|
String? url;
|
|
DateTime? lastUpdateDate;
|
|
Duration autoUpdateDuration;
|
|
UserInfo? userInfo;
|
|
bool autoUpdate;
|
|
SelectedMap selectedMap;
|
|
|
|
Profile({
|
|
String? id,
|
|
this.label,
|
|
this.url,
|
|
this.userInfo,
|
|
this.proxyName,
|
|
this.lastUpdateDate,
|
|
SelectedMap? selectedMap,
|
|
Duration? autoUpdateDuration,
|
|
this.autoUpdate = true,
|
|
}) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(),
|
|
autoUpdateDuration = autoUpdateDuration ?? defaultUpdateDuration,
|
|
selectedMap = selectedMap ?? {};
|
|
|
|
ProfileType get type => url == null ? ProfileType.file : ProfileType.url;
|
|
|
|
Future<void> checkAndUpdate() async {
|
|
final isExists = await check();
|
|
if (!isExists) {
|
|
if (url != null) {
|
|
return await update();
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> update() async {
|
|
final response = await request.getFileResponseForUrl(url!);
|
|
final disposition = response.headers.value("content-disposition");
|
|
label ??= other.getFileNameForDisposition(disposition) ?? id;
|
|
final userinfo = response.headers.value('subscription-userinfo');
|
|
userInfo = UserInfo.formHString(userinfo);
|
|
await saveFile(response.data);
|
|
lastUpdateDate = DateTime.now();
|
|
}
|
|
|
|
Future<bool> check() async {
|
|
final profilePath = await appPath.getProfilePath(id);
|
|
return await File(profilePath!).exists();
|
|
}
|
|
|
|
Future<void> saveFile(Uint8List bytes) async {
|
|
final message = await clashCore.validateConfig(utf8.decode(bytes));
|
|
if (message.isNotEmpty) {
|
|
throw message;
|
|
}
|
|
final path = await appPath.getProfilePath(id);
|
|
final file = File(path!);
|
|
final isExists = await file.exists();
|
|
if (!isExists) {
|
|
await file.create(recursive: true);
|
|
}
|
|
await file.writeAsBytes(bytes);
|
|
lastUpdateDate = DateTime.now();
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return _$ProfileToJson(this);
|
|
}
|
|
|
|
factory Profile.fromJson(Map<String, dynamic> json) {
|
|
return _$ProfileFromJson(json);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is Profile &&
|
|
runtimeType == other.runtimeType &&
|
|
id == other.id &&
|
|
label == other.label &&
|
|
proxyName == other.proxyName &&
|
|
url == other.url &&
|
|
lastUpdateDate == other.lastUpdateDate &&
|
|
autoUpdateDuration == other.autoUpdateDuration &&
|
|
userInfo == other.userInfo &&
|
|
autoUpdate == other.autoUpdate;
|
|
|
|
@override
|
|
int get hashCode =>
|
|
id.hashCode ^
|
|
label.hashCode ^
|
|
proxyName.hashCode ^
|
|
url.hashCode ^
|
|
lastUpdateDate.hashCode ^
|
|
autoUpdateDuration.hashCode ^
|
|
userInfo.hashCode ^
|
|
autoUpdate.hashCode;
|
|
|
|
@override
|
|
String toString() {
|
|
return 'Profile{id: $id, label: $label, proxyName: $proxyName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, userInfo: $userInfo, autoUpdate: $autoUpdate}';
|
|
}
|
|
|
|
Profile copyWith({
|
|
String? label,
|
|
String? url,
|
|
UserInfo? userInfo,
|
|
String? groupName,
|
|
String? proxyName,
|
|
DateTime? lastUpdateDate,
|
|
Duration? autoUpdateDuration,
|
|
bool? autoUpdate,
|
|
SelectedMap? selectedMap,
|
|
}) {
|
|
return Profile(
|
|
id: id,
|
|
label: label ?? this.label,
|
|
url: url ?? this.url,
|
|
proxyName: proxyName ?? this.proxyName,
|
|
userInfo: userInfo ?? this.userInfo,
|
|
selectedMap: selectedMap ?? this.selectedMap,
|
|
lastUpdateDate: lastUpdateDate ?? this.lastUpdateDate,
|
|
autoUpdateDuration: autoUpdateDuration ?? this.autoUpdateDuration,
|
|
autoUpdate: autoUpdate ?? this.autoUpdate,
|
|
);
|
|
}
|
|
}
|