mirror of
https://gitlab.com/foxixus/neomovies_mobile.git
synced 2025-10-28 01:18:50 +05:00
- Fix torrent platform service integration with Android engine - Add downloads page with torrent list and progress tracking - Implement torrent detail screen with file selection and priorities - Create native video player with fullscreen controls - Add WebView players for Vibix and Alloha - Integrate corrected torrent engine with file selector - Update dependencies for auto_route and video players Features: ✅ Downloads screen with real-time torrent status ✅ File-level priority management and selection ✅ Three player options: native, Vibix WebView, Alloha WebView ✅ Torrent pause/resume/remove functionality ✅ Progress tracking and seeder/peer counts ✅ Video file detection and playback integration ✅ Fixed Android torrent engine method calls This resolves torrent integration issues and provides complete downloads management UI with video playback capabilities.
221 lines
5.6 KiB
Dart
221 lines
5.6 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/foundation.dart';
|
|
import '../../data/services/torrent_platform_service.dart';
|
|
import '../../data/models/torrent_info.dart';
|
|
|
|
class ActiveDownload {
|
|
final String infoHash;
|
|
final String name;
|
|
final DownloadProgress progress;
|
|
final DateTime startTime;
|
|
final List<String> selectedFiles;
|
|
|
|
ActiveDownload({
|
|
required this.infoHash,
|
|
required this.name,
|
|
required this.progress,
|
|
required this.startTime,
|
|
required this.selectedFiles,
|
|
});
|
|
|
|
ActiveDownload copyWith({
|
|
String? infoHash,
|
|
String? name,
|
|
DownloadProgress? progress,
|
|
DateTime? startTime,
|
|
List<String>? selectedFiles,
|
|
}) {
|
|
return ActiveDownload(
|
|
infoHash: infoHash ?? this.infoHash,
|
|
name: name ?? this.name,
|
|
progress: progress ?? this.progress,
|
|
startTime: startTime ?? this.startTime,
|
|
selectedFiles: selectedFiles ?? this.selectedFiles,
|
|
);
|
|
}
|
|
}
|
|
|
|
class DownloadsProvider with ChangeNotifier {
|
|
final List<TorrentInfo> _torrents = [];
|
|
Timer? _progressTimer;
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
|
|
List<TorrentInfo> get torrents => List.unmodifiable(_torrents);
|
|
bool get isLoading => _isLoading;
|
|
String? get error => _error;
|
|
|
|
DownloadsProvider() {
|
|
_startProgressUpdates();
|
|
loadDownloads();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_progressTimer?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
void _startProgressUpdates() {
|
|
_progressTimer = Timer.periodic(const Duration(seconds: 2), (timer) {
|
|
_updateProgress();
|
|
});
|
|
}
|
|
|
|
Future<void> loadDownloads() async {
|
|
_isLoading = true;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
final progressList = await TorrentPlatformService.getAllDownloads();
|
|
|
|
_downloads = progressList.map((progress) {
|
|
// Try to find existing download to preserve metadata
|
|
final existing = _downloads.where((d) => d.infoHash == progress.infoHash).firstOrNull;
|
|
|
|
return ActiveDownload(
|
|
infoHash: progress.infoHash,
|
|
name: existing?.name ?? 'Unnamed Torrent',
|
|
progress: progress,
|
|
startTime: existing?.startTime ?? DateTime.now(),
|
|
selectedFiles: existing?.selectedFiles ?? [],
|
|
);
|
|
}).toList();
|
|
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
Future<void> _updateProgress() async {
|
|
if (_downloads.isEmpty) return;
|
|
|
|
try {
|
|
final List<ActiveDownload> updatedDownloads = [];
|
|
|
|
for (final download in _downloads) {
|
|
final progress = await TorrentPlatformService.getDownloadProgress(download.infoHash);
|
|
if (progress != null) {
|
|
updatedDownloads.add(download.copyWith(progress: progress));
|
|
}
|
|
}
|
|
|
|
_downloads = updatedDownloads;
|
|
notifyListeners();
|
|
} catch (e) {
|
|
// Silent failure for progress updates
|
|
if (kDebugMode) {
|
|
print('Failed to update progress: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<bool> pauseDownload(String infoHash) async {
|
|
try {
|
|
final success = await TorrentPlatformService.pauseDownload(infoHash);
|
|
if (success) {
|
|
await _updateProgress();
|
|
}
|
|
return success;
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
notifyListeners();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> resumeDownload(String infoHash) async {
|
|
try {
|
|
final success = await TorrentPlatformService.resumeDownload(infoHash);
|
|
if (success) {
|
|
await _updateProgress();
|
|
}
|
|
return success;
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
notifyListeners();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool> cancelDownload(String infoHash) async {
|
|
try {
|
|
final success = await TorrentPlatformService.cancelDownload(infoHash);
|
|
if (success) {
|
|
_downloads.removeWhere((d) => d.infoHash == infoHash);
|
|
notifyListeners();
|
|
}
|
|
return success;
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
notifyListeners();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void addDownload({
|
|
required String infoHash,
|
|
required String name,
|
|
required List<String> selectedFiles,
|
|
}) {
|
|
final download = ActiveDownload(
|
|
infoHash: infoHash,
|
|
name: name,
|
|
progress: DownloadProgress(
|
|
infoHash: infoHash,
|
|
progress: 0.0,
|
|
downloadRate: 0,
|
|
uploadRate: 0,
|
|
numSeeds: 0,
|
|
numPeers: 0,
|
|
state: 'starting',
|
|
),
|
|
startTime: DateTime.now(),
|
|
selectedFiles: selectedFiles,
|
|
);
|
|
|
|
_downloads.add(download);
|
|
notifyListeners();
|
|
}
|
|
|
|
ActiveDownload? getDownload(String infoHash) {
|
|
try {
|
|
return _downloads.where((d) => d.infoHash == infoHash).first;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
String formatFileSize(int bytes) {
|
|
if (bytes < 1024) return '$bytes B';
|
|
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
|
if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
|
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
|
|
}
|
|
|
|
String formatSpeed(int bytesPerSecond) {
|
|
return '${formatFileSize(bytesPerSecond)}/s';
|
|
}
|
|
|
|
String formatDuration(Duration duration) {
|
|
if (duration.inDays > 0) {
|
|
return '${duration.inDays}d ${duration.inHours % 24}h';
|
|
}
|
|
if (duration.inHours > 0) {
|
|
return '${duration.inHours}h ${duration.inMinutes % 60}m';
|
|
}
|
|
if (duration.inMinutes > 0) {
|
|
return '${duration.inMinutes}m ${duration.inSeconds % 60}s';
|
|
}
|
|
return '${duration.inSeconds}s';
|
|
}
|
|
}
|
|
|
|
extension ListExtension<T> on List<T> {
|
|
T? get firstOrNull => isEmpty ? null : first;
|
|
} |