mirror of
https://gitlab.com/foxixus/neomovies_mobile.git
synced 2025-10-28 03:18:49 +05:00
164 lines
5.6 KiB
Dart
164 lines
5.6 KiB
Dart
import 'dart:convert';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
import '../models/torrent.dart';
|
|
|
|
class TorrentService {
|
|
static const String _baseUrl = 'API_URL';
|
|
|
|
String get apiUrl => dotenv.env[_baseUrl] ?? 'http://localhost:3000';
|
|
|
|
/// Получить торренты по IMDB ID
|
|
/// [imdbId] - IMDB ID фильма/сериала (например, 'tt1234567')
|
|
/// [type] - тип контента: 'movie' или 'tv'
|
|
/// [season] - номер сезона для сериалов (опционально)
|
|
Future<List<Torrent>> getTorrents({
|
|
required String imdbId,
|
|
required String type,
|
|
int? season,
|
|
}) async {
|
|
try {
|
|
final uri = Uri.parse('$apiUrl/torrents/search/$imdbId').replace(
|
|
queryParameters: {
|
|
'type': type,
|
|
if (season != null) 'season': season.toString(),
|
|
},
|
|
);
|
|
|
|
final response = await http.get(
|
|
uri,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
).timeout(const Duration(seconds: 10));
|
|
|
|
if (response.statusCode == 200) {
|
|
final data = json.decode(response.body);
|
|
final results = data['results'] as List<dynamic>? ?? [];
|
|
|
|
return results
|
|
.map((json) => Torrent.fromJson(json as Map<String, dynamic>))
|
|
.toList();
|
|
} else if (response.statusCode == 404) {
|
|
// Торренты не найдены - возвращаем пустой список
|
|
return [];
|
|
} else {
|
|
throw Exception('HTTP ${response.statusCode}: ${response.body}');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Ошибка загрузки торрентов: $e');
|
|
}
|
|
}
|
|
|
|
/// Определить качество из названия торрента
|
|
String? detectQuality(String title) {
|
|
final titleLower = title.toLowerCase();
|
|
|
|
// Порядок важен - сначала более специфичные паттерны
|
|
if (titleLower.contains('2160p') || titleLower.contains('4k')) {
|
|
return '4K';
|
|
}
|
|
if (titleLower.contains('1440p') || titleLower.contains('2k')) {
|
|
return '1440p';
|
|
}
|
|
if (titleLower.contains('1080p')) {
|
|
return '1080p';
|
|
}
|
|
if (titleLower.contains('720p')) {
|
|
return '720p';
|
|
}
|
|
if (titleLower.contains('480p')) {
|
|
return '480p';
|
|
}
|
|
if (titleLower.contains('360p')) {
|
|
return '360p';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// Форматировать размер из байтов в читаемый формат
|
|
String formatFileSize(int? sizeInBytes) {
|
|
if (sizeInBytes == null || sizeInBytes == 0) return 'Неизвестно';
|
|
|
|
const int kb = 1024;
|
|
const int mb = kb * 1024;
|
|
const int gb = mb * 1024;
|
|
|
|
if (sizeInBytes >= gb) {
|
|
return '${(sizeInBytes / gb).toStringAsFixed(1)} GB';
|
|
} else if (sizeInBytes >= mb) {
|
|
return '${(sizeInBytes / mb).toStringAsFixed(0)} MB';
|
|
} else if (sizeInBytes >= kb) {
|
|
return '${(sizeInBytes / kb).toStringAsFixed(0)} KB';
|
|
} else {
|
|
return '$sizeInBytes B';
|
|
}
|
|
}
|
|
|
|
/// Группировать торренты по качеству
|
|
Map<String, List<Torrent>> groupTorrentsByQuality(List<Torrent> torrents) {
|
|
final groups = <String, List<Torrent>>{};
|
|
|
|
for (final torrent in torrents) {
|
|
final title = torrent.title ?? torrent.name ?? '';
|
|
final quality = detectQuality(title) ?? 'Неизвестно';
|
|
|
|
if (!groups.containsKey(quality)) {
|
|
groups[quality] = [];
|
|
}
|
|
groups[quality]!.add(torrent);
|
|
}
|
|
|
|
// Сортируем торренты внутри каждой группы по количеству сидов (убывание)
|
|
for (final group in groups.values) {
|
|
group.sort((a, b) => (b.seeders ?? 0).compareTo(a.seeders ?? 0));
|
|
}
|
|
|
|
// Возвращаем группы в порядке качества (от высокого к низкому)
|
|
final sortedGroups = <String, List<Torrent>>{};
|
|
const qualityOrder = ['4K', '1440p', '1080p', '720p', '480p', '360p', 'Неизвестно'];
|
|
|
|
for (final quality in qualityOrder) {
|
|
if (groups.containsKey(quality) && groups[quality]!.isNotEmpty) {
|
|
sortedGroups[quality] = groups[quality]!;
|
|
}
|
|
}
|
|
|
|
return sortedGroups;
|
|
}
|
|
|
|
/// Получить доступные сезоны для сериала
|
|
/// [imdbId] - IMDB ID сериала
|
|
Future<List<int>> getAvailableSeasons(String imdbId) async {
|
|
try {
|
|
// Получаем все торренты для сериала без указания сезона
|
|
final torrents = await getTorrents(imdbId: imdbId, type: 'tv');
|
|
|
|
// Извлекаем номера сезонов из названий торрентов
|
|
final seasons = <int>{};
|
|
|
|
for (final torrent in torrents) {
|
|
final title = torrent.title ?? torrent.name ?? '';
|
|
final seasonRegex = RegExp(r'(?:s|сезон)[\s:]*(\d+)|(\d+)\s*сезон', caseSensitive: false);
|
|
final matches = seasonRegex.allMatches(title);
|
|
|
|
for (final match in matches) {
|
|
final seasonStr = match.group(1) ?? match.group(2);
|
|
if (seasonStr != null) {
|
|
final seasonNumber = int.tryParse(seasonStr);
|
|
if (seasonNumber != null && seasonNumber > 0) {
|
|
seasons.add(seasonNumber);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
final sortedSeasons = seasons.toList()..sort();
|
|
return sortedSeasons;
|
|
} catch (e) {
|
|
throw Exception('Ошибка получения списка сезонов: $e');
|
|
}
|
|
}
|
|
}
|