new: add link parser and allow custom configs to be imported
This commit is contained in:
@@ -361,7 +361,7 @@ class ProfileRepositoryImpl
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final _subInfoHeaders = [
|
static final _subInfoHeaders = [
|
||||||
'profile-title',
|
'profile-title',
|
||||||
'content-disposition',
|
'content-disposition',
|
||||||
'subscription-userinfo',
|
'subscription-userinfo',
|
||||||
@@ -418,8 +418,20 @@ class ProfileRepositoryImpl
|
|||||||
"only [$headersFound] headers found, checking file content for possible information",
|
"only [$headersFound] headers found, checking file content for possible information",
|
||||||
);
|
);
|
||||||
var content = await File(path).readAsString();
|
var content = await File(path).readAsString();
|
||||||
content = safeDecodeBase64(content);
|
final contentHeaders = parseHeadersFromContent(content);
|
||||||
final lines = content.split("\n");
|
for (final entry in contentHeaders.entries) {
|
||||||
|
if (!headers.keys.contains(entry.key) && entry.value.isNotEmpty) {
|
||||||
|
headers[entry.key] = entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, List<String>> parseHeadersFromContent(String content) {
|
||||||
|
final headers = <String, List<String>>{};
|
||||||
|
final content_ = safeDecodeBase64(content);
|
||||||
|
final lines = content_.split("\n");
|
||||||
final linesToProcess = lines.length < 10 ? lines.length : 10;
|
final linesToProcess = lines.length < 10 ? lines.length : 10;
|
||||||
for (int i = 0; i < linesToProcess; i++) {
|
for (int i = 0; i < linesToProcess; i++) {
|
||||||
final line = lines[i];
|
final line = lines[i];
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:dartx/dartx.dart';
|
||||||
|
import 'package:hiddify/features/profile/data/profile_parser.dart';
|
||||||
|
import 'package:hiddify/features/profile/data/profile_repository.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_proxy_type.dart';
|
import 'package:hiddify/singbox/model/singbox_proxy_type.dart';
|
||||||
import 'package:hiddify/utils/validators.dart';
|
import 'package:hiddify/utils/validators.dart';
|
||||||
|
|
||||||
@@ -15,16 +18,14 @@ abstract class LinkParser {
|
|||||||
host: uri.host,
|
host: uri.host,
|
||||||
path: uri.path,
|
path: uri.path,
|
||||||
query: uri.query,
|
query: uri.query,
|
||||||
fragment: name??uri.fragment,
|
fragment: name ?? uri.fragment,
|
||||||
);
|
);
|
||||||
// return 'hiddify://import/$modifiedUri';
|
// return 'hiddify://import/$modifiedUri';
|
||||||
return '$modifiedUri';
|
return '$modifiedUri';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// protocols schemas
|
// protocols schemas
|
||||||
static const protocols = {'clash', 'clashmeta', 'sing-box', 'hiddify'};
|
static const protocols = {'clash', 'clashmeta', 'sing-box', 'hiddify'};
|
||||||
|
|
||||||
|
|
||||||
static ProfileLink? parse(String link) {
|
static ProfileLink? parse(String link) {
|
||||||
return simple(link) ?? deep(link);
|
return simple(link) ?? deep(link);
|
||||||
@@ -40,31 +41,39 @@ abstract class LinkParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ({String content, String name})? protocol(String content) {
|
static ({String content, String name})? protocol(String content) {
|
||||||
final lines = safeDecodeBase64(content).split('\n');
|
final normalContent = safeDecodeBase64(content);
|
||||||
|
final lines = normalContent.split('\n');
|
||||||
|
String? name;
|
||||||
for (final line in lines) {
|
for (final line in lines) {
|
||||||
final uri = Uri.tryParse(line);
|
final uri = Uri.tryParse(line);
|
||||||
if (uri == null) continue;
|
if (uri == null) continue;
|
||||||
final fragment =
|
final fragment =
|
||||||
uri.hasFragment ? Uri.decodeComponent(uri.fragment) : null;
|
uri.hasFragment ? Uri.decodeComponent(uri.fragment) : null;
|
||||||
final name = switch (uri.scheme) {
|
|
||||||
'ss' => fragment ?? ProxyType.shadowsocks.label,
|
|
||||||
'ssconf' => fragment ?? ProxyType.shadowsocks.label,
|
|
||||||
'vmess' => ProxyType.vmess.label,
|
|
||||||
'vless' => fragment ?? ProxyType.vless.label,
|
|
||||||
'trojan' => fragment ?? ProxyType.trojan.label,
|
|
||||||
'tuic' => fragment ?? ProxyType.tuic.label,
|
|
||||||
'hy2' || 'hysteria2' => fragment ?? ProxyType.hysteria2.label,
|
|
||||||
'hy' || 'hysteria' => fragment ?? ProxyType.hysteria.label,
|
|
||||||
'ssh' => fragment ?? ProxyType.ssh.label,
|
|
||||||
'wg' => fragment ?? ProxyType.wireguard.label,
|
|
||||||
'warp'=>fragment ?? ProxyType.warp.label,
|
|
||||||
_ => ProxyType.unknown.label,
|
|
||||||
};
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
return (content: content, name: name);
|
name = switch (uri.scheme) {
|
||||||
|
'ss' => fragment ?? ProxyType.shadowsocks.label,
|
||||||
|
'ssconf' => fragment ?? ProxyType.shadowsocks.label,
|
||||||
|
'vmess' => ProxyType.vmess.label,
|
||||||
|
'vless' => fragment ?? ProxyType.vless.label,
|
||||||
|
'trojan' => fragment ?? ProxyType.trojan.label,
|
||||||
|
'tuic' => fragment ?? ProxyType.tuic.label,
|
||||||
|
'hy2' || 'hysteria2' => fragment ?? ProxyType.hysteria2.label,
|
||||||
|
'hy' || 'hysteria' => fragment ?? ProxyType.hysteria.label,
|
||||||
|
'ssh' => fragment ?? ProxyType.ssh.label,
|
||||||
|
'wg' => fragment ?? ProxyType.wireguard.label,
|
||||||
|
'warp' => fragment ?? ProxyType.warp.label,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
final headers = ProfileRepositoryImpl.parseHeadersFromContent(content);
|
||||||
|
final subinfo = ProfileParser.parse("", headers);
|
||||||
|
|
||||||
|
if (subinfo.name.isNotNullOrEmpty) {
|
||||||
|
name = subinfo.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (content: normalContent, name: name ?? ProxyType.unknown.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ProfileLink? deep(String link) {
|
static ProfileLink? deep(String link) {
|
||||||
@@ -81,9 +90,12 @@ abstract class LinkParser {
|
|||||||
!queryParams.containsKey('url')) return null;
|
!queryParams.containsKey('url')) return null;
|
||||||
return (url: queryParams['url']!, name: queryParams['name'] ?? '');
|
return (url: queryParams['url']!, name: queryParams['name'] ?? '');
|
||||||
case 'hiddify':
|
case 'hiddify':
|
||||||
if (uri.authority=="import") {
|
if (uri.authority == "import") {
|
||||||
return (url: uri.path.substring(1)+(uri.hasQuery?"?${uri.query}":""), name: uri.fragment);
|
return (
|
||||||
}
|
url: uri.path.substring(1) + (uri.hasQuery ? "?${uri.query}" : ""),
|
||||||
|
name: uri.fragment
|
||||||
|
);
|
||||||
|
}
|
||||||
//for backward compatibility
|
//for backward compatibility
|
||||||
if ((uri.authority != 'install-config' &&
|
if ((uri.authority != 'install-config' &&
|
||||||
uri.authority != 'install-sub') ||
|
uri.authority != 'install-sub') ||
|
||||||
|
|||||||
Reference in New Issue
Block a user