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; @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 toJson() { return _$UserInfoToJson(this); } factory UserInfo.fromJson(Map json) { return _$UserInfoFromJson(json); } factory UserInfo.formHString(String? info) { if (info == null) return UserInfo(); var list = info.split(";"); Map 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 checkAndUpdate() async { final isExists = await check(); if (!isExists) { if (url != null) { return await update(); } } } Future 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 check() async { final profilePath = await appPath.getProfilePath(id); return await File(profilePath!).exists(); } Future 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 toJson() { return _$ProfileToJson(this); } factory Profile.fromJson(Map 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, ); } }