Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions lib/services/api/nexusphp_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'nexusphp_helper.dart';
class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
late SiteConfig _siteConfig;
late Dio _dio;
static final RegExp _whitespaceRegExp = RegExp(r'[\s\u200B-\u200D\uFEFF]');
Map<String, String>? _discountMapping;
Map<String, String>? _tagMapping;
static final Logger _logger = Logger();
Expand All @@ -39,7 +40,9 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
await _loadTagMapping();
swDiscount.stop();
if (kDebugMode) {
_logger.d('NexusPHPAdapter.init: 加载优惠映射耗时=${swDiscount.elapsedMilliseconds}ms');
_logger.d(
'NexusPHPAdapter.init: 加载优惠映射耗时=${swDiscount.elapsedMilliseconds}ms',
);
}

_dio = Dio(
Expand Down Expand Up @@ -75,7 +78,9 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
);
swInterceptors.stop();
if (kDebugMode) {
_logger.d('NexusPHPAdapter.init: 配置Dio与拦截器耗时=${swInterceptors.elapsedMilliseconds}ms');
_logger.d(
'NexusPHPAdapter.init: 配置Dio与拦截器耗时=${swInterceptors.elapsedMilliseconds}ms',
);
}
swTotal.stop();
if (kDebugMode) {
Expand All @@ -91,9 +96,7 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
SiteType.nexusphp,
);
if (template?.discountMapping != null) {
_discountMapping = Map<String, String>.from(
template!.discountMapping,
);
_discountMapping = Map<String, String>.from(template!.discountMapping);
}
final specialMapping = await SiteConfigService.getDiscountMapping(
_siteConfig.baseUrl,
Expand Down Expand Up @@ -283,8 +286,6 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
}
}



final name = item['name'] as String;
final smallDescr = item['small_descr'] as String? ?? '';

Expand All @@ -311,8 +312,6 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
}
}



return TorrentItem(
id: (item['id'] as int).toString(),
name: name,
Expand Down Expand Up @@ -369,7 +368,11 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
}

@override
Future<TorrentCommentList> fetchComments(String id, {int pageNumber = 1, int pageSize = 20}) async {
Future<TorrentCommentList> fetchComments(
String id, {
int pageNumber = 1,
int pageSize = 20,
}) async {
try {
final response = await _dio.get(
'/api/v1/comments',
Expand Down Expand Up @@ -506,9 +509,7 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
Future<List<SearchCategoryConfig>> getSearchCategories() async {
// 通过baseUrl匹配预设配置
final defaultCategories =
await SiteConfigService.getDefaultSearchCategories(
_siteConfig.baseUrl,
);
await SiteConfigService.getDefaultSearchCategories(_siteConfig.baseUrl);

// 如果获取到默认分类配置,则直接返回
if (defaultCategories.isNotEmpty) {
Expand Down Expand Up @@ -536,13 +537,13 @@ class NexusPHPAdapter with NexusPHPHelper implements SiteAdapter {
for (final section in sectionsData) {
final sectionName = section['name'] as String;
final sectionDisplayName = (section['display_name'] as String)
.replaceAll(RegExp(r'[\s\u200B-\u200D\uFEFF]'), '');
.replaceAll(_whitespaceRegExp, '');
final categoriesData = section['categories'] as List;

for (final category in categoriesData) {
final categoryId = category['id'];
final categoryName = (category['name'] as String).replaceAll(
RegExp(r'[\s\u200B-\u200D\uFEFF]'),
_whitespaceRegExp,
'',
);
categories.add(
Expand Down
13 changes: 8 additions & 5 deletions lib/services/api/nexusphp_web_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class ParseSearchParams {
});
}

final RegExp _sizeRegExp = RegExp(r'([\d.]+)\s*(\w+)');
final RegExp _relativeUrlRegExp = RegExp(
r'(src|href)="((?!https?://|//|data:|javascript:|#)[^"]+)"',
caseSensitive: false,
);

/// 解析结果对象
class ParsedTorrentResult {
final List<TorrentItem> items;
Expand Down Expand Up @@ -1147,7 +1153,7 @@ class NexusPHPWebAdapter extends SiteAdapter
// 解析文件大小为字节数
int sizeInBytes = 0;
if (sizeText.isNotEmpty) {
final sizeMatch = RegExp(r'([\d.]+)\s*(\w+)').firstMatch(sizeText);
final sizeMatch = _sizeRegExp.firstMatch(sizeText);
if (sizeMatch != null) {
final sizeValue = double.tryParse(sizeMatch.group(1) ?? '0') ?? 0;
final unit = sizeMatch.group(2)?.toUpperCase() ?? 'B';
Expand Down Expand Up @@ -1303,10 +1309,7 @@ class NexusPHPWebAdapter extends SiteAdapter
} else {
// HTML 模式:处理相对URL
extractedContent = extractedContent.replaceAllMapped(
RegExp(
r'(src|href)="((?!https?://|//|data:|javascript:|#)[^"]+)"',
caseSensitive: false,
),
_relativeUrlRegExp,
(match) {
final attr = match.group(1);
final path = match.group(2)!;
Expand Down
10 changes: 5 additions & 5 deletions lib/services/downloader/qbittorrent_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import 'downloader_config.dart';
import 'downloader_models.dart';
import 'torrent_file_downloader_mixin.dart';

final RegExp _httpRegExp = RegExp(r'https?://');
final RegExp _sidRegExp = RegExp(r'SID=([^;]+)');

/// qBittorrent下载器客户端实现
class QbittorrentClient
with TorrentFileDownloaderMixin
Expand Down Expand Up @@ -65,7 +68,7 @@ class QbittorrentClient
String _buildBase(QbittorrentConfig c) {
var urlStr = c.host.trim();
// 补全协议
if (!urlStr.startsWith(RegExp(r'https?://'))) {
if (!urlStr.startsWith(_httpRegExp)) {
urlStr = 'http://$urlStr';
}

Expand Down Expand Up @@ -223,7 +226,7 @@ class QbittorrentClient
// 从响应头中提取会话ID
final cookies = response.headers['set-cookie'];
if (cookies != null && cookies.isNotEmpty) {
final sidMatch = RegExp(r'SID=([^;]+)').firstMatch(cookies.first);
final sidMatch = _sidRegExp.firstMatch(cookies.first);
if (sidMatch != null) {
_sessionId = sidMatch.group(1);
return;
Expand Down Expand Up @@ -308,7 +311,6 @@ class QbittorrentClient
forceRelay = true;
}


final useRelay = config.useLocalRelay || forceRelay;
if (!useRelay) {
body['urls'] = url;
Expand Down Expand Up @@ -339,8 +341,6 @@ class QbittorrentClient
await _request('POST', '/torrents/add', body: body);
}



/// 根据版本获取暂停任务的 API 路径
String _getPauseApiPath(String? version) {
if (version == null) return '/torrents/pause'; // 默认使用 4.x 的路径
Expand Down
6 changes: 3 additions & 3 deletions lib/services/downloader/rutorrent_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import 'downloader_config.dart';
import 'downloader_models.dart';
import 'torrent_file_downloader_mixin.dart';

final RegExp _httpRegExp = RegExp(r'https?://');

/// ruTorrent下载器客户端实现
class RuTorrentClient
with TorrentFileDownloaderMixin
Expand Down Expand Up @@ -66,7 +68,7 @@ class RuTorrentClient
String _buildBase(RuTorrentConfig c) {
var urlStr = c.host.trim();
// 补全协议
if (!urlStr.startsWith(RegExp(r'https?://'))) {
if (!urlStr.startsWith(_httpRegExp)) {
urlStr = 'http://$urlStr';
}

Expand Down Expand Up @@ -291,8 +293,6 @@ class RuTorrentClient
return FormatUtil.parseInt(val) ?? 0;
}



@override
Future<void> testConnection() async {
try {
Expand Down
89 changes: 53 additions & 36 deletions lib/services/downloader/transmission_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import 'downloader_config.dart';
import 'downloader_models.dart';
import 'torrent_file_downloader_mixin.dart';

final RegExp _httpRegExp = RegExp(r'https?://');

/// Transmission下载器客户端实现
class TransmissionClient
with TorrentFileDownloaderMixin
Expand All @@ -32,17 +34,19 @@ class TransmissionClient
required this.password,
Function(TransmissionConfig)? onConfigUpdated,
}) : _onConfigUpdated = onConfigUpdated {
_dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 30),
headers: {
_dio = Dio(
BaseOptions(
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 30),
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36',
'Content-Type': 'application/json',
},
'Content-Type': 'application/json',
},
followRedirects: true,
maxRedirects: 5,
));
),
);
}

/// 获取基础URL
Expand All @@ -52,7 +56,7 @@ class TransmissionClient
String _buildBase(TransmissionConfig c) {
var urlStr = c.host.trim();
// 补全协议
if (!urlStr.startsWith(RegExp(r'https?://'))) {
if (!urlStr.startsWith(_httpRegExp)) {
urlStr = 'http://$urlStr';
}

Expand Down Expand Up @@ -85,18 +89,15 @@ class TransmissionClient
}) async {
final url = '$_baseUrl$_rpcPath';

final requestBody = {
'method': method,
'arguments': ?arguments,
};
final requestBody = {'method': method, 'arguments': ?arguments};

final requestHeaders = <String, String>{
'Content-Type': 'application/json',
};
final requestHeaders = <String, String>{'Content-Type': 'application/json'};

// 添加认证信息
if (config.username.isNotEmpty) {
final credentials = base64Encode(utf8.encode('${config.username}:$password'));
final credentials = base64Encode(
utf8.encode('${config.username}:$password'),
);
requestHeaders['Authorization'] = 'Basic $credentials';
}

Expand Down Expand Up @@ -129,7 +130,9 @@ class TransmissionClient
// 检查是否需要会话ID
if (e.response?.statusCode == 409) {
// 从响应头中提取会话ID
final sessionId = e.response?.headers.value('X-Transmission-Session-Id');
final sessionId = e.response?.headers.value(
'X-Transmission-Session-Id',
);
if (sessionId != null) {
_sessionId = sessionId;
// 重试请求
Expand All @@ -142,15 +145,15 @@ class TransmissionClient
}

if (e.response?.statusCode != null && e.response!.statusCode! >= 400) {
throw HttpException('HTTP ${e.response!.statusCode}: ${e.response!.data}');
throw HttpException(
'HTTP ${e.response!.statusCode}: ${e.response!.data}',
);
}

throw Exception('Request failed: ${e.message}');
}
}



@override
Future<void> testConnection() async {
try {
Expand Down Expand Up @@ -283,13 +286,16 @@ class TransmissionClient
}

@override
Future<void> deleteTasks(List<String> hashes, {bool deleteFiles = false}) async {
Future<void> deleteTasks(
List<String> hashes, {
bool deleteFiles = false,
}) async {
final ids = await _hashesToIds(hashes);
if (ids.isNotEmpty) {
await _rpcRequest('torrent-remove', arguments: {
'ids': ids,
'delete-local-data': deleteFiles,
});
await _rpcRequest(
'torrent-remove',
arguments: {'ids': ids, 'delete-local-data': deleteFiles},
);
}
}

Expand All @@ -303,9 +309,12 @@ class TransmissionClient
@override
Future<List<String>> getTags() async {
// 获取所有种子的标签
final response = await _rpcRequest('torrent-get', arguments: {
'fields': ['labels'],
});
final response = await _rpcRequest(
'torrent-get',
arguments: {
'fields': ['labels'],
},
);

final List<dynamic> torrents = response['torrents'] as List<dynamic>? ?? [];
final Set<String> allLabels = {};
Expand Down Expand Up @@ -346,9 +355,12 @@ class TransmissionClient
@override
Future<List<String>> getPaths() async {
// 获取所有种子的下载路径
final response = await _rpcRequest('torrent-get', arguments: {
'fields': ['downloadDir'],
});
final response = await _rpcRequest(
'torrent-get',
arguments: {
'fields': ['downloadDir'],
},
);

final List<dynamic> torrents = response['torrents'] as List<dynamic>? ?? [];
final Set<String> allPaths = {};
Expand Down Expand Up @@ -386,9 +398,12 @@ class TransmissionClient
Future<List<int>> _hashesToIds(List<String> hashes) async {
if (hashes.isEmpty) return [];

final response = await _rpcRequest('torrent-get', arguments: {
'fields': ['id', 'hashString'],
});
final response = await _rpcRequest(
'torrent-get',
arguments: {
'fields': ['id', 'hashString'],
},
);

final List<dynamic> torrents = response['torrents'] as List<dynamic>? ?? [];
final List<int> ids = [];
Expand Down Expand Up @@ -457,7 +472,9 @@ class TransmissionClient
addedOn: torrent['addedDate'] ?? 0,
amountLeft: leftUntilDone,
ratio: (torrent['uploadRatio'] as num? ?? 0).toDouble(),
timeActive: (torrent['activityDate'] as int? ?? 0) - (torrent['addedDate'] as int? ?? 0),
timeActive:
(torrent['activityDate'] as int? ?? 0) -
(torrent['addedDate'] as int? ?? 0),
uploaded: torrent['uploadedEver'] ?? 0,
);
}
Expand Down Expand Up @@ -486,4 +503,4 @@ class TransmissionClient
void dispose() {
_dio.close();
}
}
}
Loading