2025-10-03 06:40:56 +00:00
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
|
import '../../data/services/torrent_platform_service.dart';
|
|
|
|
|
|
import '../../data/models/torrent_info.dart';
|
|
|
|
|
|
|
|
|
|
|
|
/// Provider для управления загрузками торрентов
|
|
|
|
|
|
class DownloadsProvider with ChangeNotifier {
|
|
|
|
|
|
final List<TorrentInfo> _torrents = [];
|
|
|
|
|
|
Timer? _progressTimer;
|
|
|
|
|
|
bool _isLoading = false;
|
|
|
|
|
|
String? _error;
|
2025-10-05 16:49:43 +00:00
|
|
|
|
String? _stackTrace;
|
2025-10-03 06:40:56 +00:00
|
|
|
|
|
|
|
|
|
|
List<TorrentInfo> get torrents => List.unmodifiable(_torrents);
|
|
|
|
|
|
bool get isLoading => _isLoading;
|
|
|
|
|
|
String? get error => _error;
|
2025-10-05 16:49:43 +00:00
|
|
|
|
String? get stackTrace => _stackTrace;
|
2025-10-03 06:40:56 +00:00
|
|
|
|
|
|
|
|
|
|
DownloadsProvider() {
|
|
|
|
|
|
_startProgressUpdates();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
|
_progressTimer?.cancel();
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _startProgressUpdates() {
|
|
|
|
|
|
_progressTimer = Timer.periodic(const Duration(seconds: 3), (timer) {
|
|
|
|
|
|
if (_torrents.isNotEmpty && !_isLoading) {
|
|
|
|
|
|
refreshDownloads();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Загрузить список активных загрузок
|
|
|
|
|
|
Future<void> refreshDownloads() async {
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 DownloadsProvider: refreshDownloads() called');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
try {
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 Setting loading=true, error=null');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
_setLoading(true);
|
|
|
|
|
|
_setError(null);
|
|
|
|
|
|
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 Calling TorrentPlatformService.getAllDownloads()...');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
final progress = await TorrentPlatformService.getAllDownloads();
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 Got ${progress.length} torrents from platform service');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
|
|
|
|
|
|
// Получаем полную информацию о каждом торренте
|
|
|
|
|
|
_torrents.clear();
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 Cleared _torrents list');
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < progress.length; i++) {
|
|
|
|
|
|
final progressItem = progress[i];
|
|
|
|
|
|
print('📥 Processing torrent $i: ${progressItem.infoHash.substring(0, 8)}...');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
try {
|
|
|
|
|
|
final torrentInfo = await TorrentPlatformService.getTorrent(progressItem.infoHash);
|
|
|
|
|
|
if (torrentInfo != null) {
|
|
|
|
|
|
_torrents.add(torrentInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// Если не удалось получить полную информацию, создаем базовую
|
|
|
|
|
|
_torrents.add(TorrentInfo(
|
|
|
|
|
|
infoHash: progressItem.infoHash,
|
|
|
|
|
|
name: 'Торрент ${progressItem.infoHash.substring(0, 8)}',
|
|
|
|
|
|
totalSize: 0,
|
|
|
|
|
|
progress: progressItem.progress,
|
|
|
|
|
|
downloadSpeed: progressItem.downloadRate,
|
|
|
|
|
|
uploadSpeed: progressItem.uploadRate,
|
|
|
|
|
|
numSeeds: progressItem.numSeeds,
|
|
|
|
|
|
numPeers: progressItem.numPeers,
|
|
|
|
|
|
state: progressItem.state,
|
|
|
|
|
|
savePath: '/storage/emulated/0/Download/NeoMovies',
|
|
|
|
|
|
files: [],
|
2025-10-05 17:35:07 +00:00
|
|
|
|
);
|
|
|
|
|
|
print('📥 ✅ Created basic info: ${basicInfo.name}');
|
|
|
|
|
|
_torrents.add(basicInfo);
|
2025-10-03 06:40:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 Final torrents count: ${_torrents.length}');
|
|
|
|
|
|
print('📥 Setting loading=false');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
_setLoading(false);
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 ✅ refreshDownloads() completed successfully');
|
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
|
print('📥 ❌ Error refreshing downloads: $e');
|
|
|
|
|
|
print('📥 Stack trace: $stackTrace');
|
|
|
|
|
|
_setError(e.toString(), stackTrace.toString());
|
2025-10-03 06:40:56 +00:00
|
|
|
|
_setLoading(false);
|
2025-10-05 17:35:07 +00:00
|
|
|
|
print('📥 Error state set, loading=false');
|
2025-10-03 06:40:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Получить информацию о конкретном торренте
|
|
|
|
|
|
Future<TorrentInfo?> getTorrentInfo(String infoHash) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return await TorrentPlatformService.getTorrent(infoHash);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
debugPrint('Ошибка получения информации о торренте: $e');
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Приостановить торрент
|
|
|
|
|
|
Future<void> pauseTorrent(String infoHash) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await TorrentPlatformService.pauseDownload(infoHash);
|
|
|
|
|
|
await refreshDownloads(); // Обновляем список
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_setError(e.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Возобновить торрент
|
|
|
|
|
|
Future<void> resumeTorrent(String infoHash) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await TorrentPlatformService.resumeDownload(infoHash);
|
|
|
|
|
|
await refreshDownloads(); // Обновляем список
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_setError(e.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Удалить торрент
|
|
|
|
|
|
Future<void> removeTorrent(String infoHash) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await TorrentPlatformService.cancelDownload(infoHash);
|
|
|
|
|
|
await refreshDownloads(); // Обновляем список
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_setError(e.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Установить приоритет файла
|
|
|
|
|
|
Future<void> setFilePriority(String infoHash, int fileIndex, FilePriority priority) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await TorrentPlatformService.setFilePriority(infoHash, fileIndex, priority);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_setError(e.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Добавить новый торрент
|
|
|
|
|
|
Future<String?> addTorrent(String magnetUri, {String? savePath}) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
final infoHash = await TorrentPlatformService.addTorrent(
|
|
|
|
|
|
magnetUri: magnetUri,
|
|
|
|
|
|
savePath: savePath,
|
|
|
|
|
|
);
|
|
|
|
|
|
await refreshDownloads(); // Обновляем список
|
|
|
|
|
|
return infoHash;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
_setError(e.toString());
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Форматировать скорость
|
|
|
|
|
|
String formatSpeed(int bytesPerSecond) {
|
|
|
|
|
|
if (bytesPerSecond < 1024) return '${bytesPerSecond}B/s';
|
|
|
|
|
|
if (bytesPerSecond < 1024 * 1024) return '${(bytesPerSecond / 1024).toStringAsFixed(1)}KB/s';
|
|
|
|
|
|
return '${(bytesPerSecond / (1024 * 1024)).toStringAsFixed(1)}MB/s';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Форматировать продолжительность
|
|
|
|
|
|
String formatDuration(Duration duration) {
|
|
|
|
|
|
final hours = duration.inHours;
|
|
|
|
|
|
final minutes = duration.inMinutes.remainder(60);
|
|
|
|
|
|
final seconds = duration.inSeconds.remainder(60);
|
|
|
|
|
|
|
|
|
|
|
|
if (hours > 0) {
|
|
|
|
|
|
return '${hours}ч ${minutes}м ${seconds}с';
|
|
|
|
|
|
} else if (minutes > 0) {
|
|
|
|
|
|
return '${minutes}м ${seconds}с';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return '${seconds}с';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void _setLoading(bool loading) {
|
|
|
|
|
|
_isLoading = loading;
|
|
|
|
|
|
notifyListeners();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-05 16:59:36 +00:00
|
|
|
|
void _setError(String? error, [String? stackTrace]) {
|
2025-10-05 16:49:43 +00:00
|
|
|
|
_error = error;
|
2025-10-05 16:59:36 +00:00
|
|
|
|
_stackTrace = stackTrace;
|
2025-10-05 16:49:43 +00:00
|
|
|
|
notifyListeners();
|
|
|
|
|
|
}
|
2025-10-03 06:40:56 +00:00
|
|
|
|
}
|