Initial commit: Xtream IPTV Player for Android TV
This commit is contained in:
174
lib/services/iptv_provider.dart
Normal file
174
lib/services/iptv_provider.dart
Normal file
@@ -0,0 +1,174 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../services/xtream_api.dart';
|
||||
import '../models/xtream_models.dart';
|
||||
|
||||
enum ContentType { live, movies, series }
|
||||
|
||||
class IPTVProvider extends ChangeNotifier {
|
||||
final XtreamApiService _api = XtreamApiService();
|
||||
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
XtreamUserInfo? _userInfo;
|
||||
|
||||
List<XtreamCategory> _liveCategories = [];
|
||||
List<XtreamCategory> _vodCategories = [];
|
||||
List<XtreamCategory> _seriesCategories = [];
|
||||
|
||||
List<XtreamStream> _liveStreams = [];
|
||||
List<XtreamStream> _vodStreams = [];
|
||||
List<XtreamSeries> _seriesList = [];
|
||||
List<XtreamEpisode> _seriesEpisodes = [];
|
||||
|
||||
String _selectedLiveCategory = '';
|
||||
String _selectedVodCategory = '';
|
||||
XtreamSeries? _selectedSeries;
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
XtreamUserInfo? get userInfo => _userInfo;
|
||||
XtreamApiService get api => _api;
|
||||
|
||||
List<XtreamCategory> get liveCategories => _liveCategories;
|
||||
List<XtreamCategory> get vodCategories => _vodCategories;
|
||||
List<XtreamCategory> get seriesCategories => _seriesCategories;
|
||||
|
||||
List<XtreamStream> get liveStreams => _liveStreams;
|
||||
List<XtreamStream> get vodStreams => _vodStreams;
|
||||
List<XtreamSeries> get seriesList => _seriesList;
|
||||
List<XtreamEpisode> get seriesEpisodes => _seriesEpisodes;
|
||||
|
||||
String get selectedLiveCategory => _selectedLiveCategory;
|
||||
String get selectedVodCategory => _selectedVodCategory;
|
||||
XtreamSeries? get selectedSeries => _selectedSeries;
|
||||
|
||||
Future<void> login(String server, String username, String password) async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_api.setCredentials(server, username, password);
|
||||
_userInfo = await _api.getUserInfo();
|
||||
|
||||
await _loadCategories();
|
||||
await _saveCredentials(server, username, password);
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _loadCategories() async {
|
||||
try {
|
||||
_liveCategories = await _api.getLiveCategories();
|
||||
_vodCategories = await _api.getVodCategories();
|
||||
_seriesCategories = await _api.getSeriesCategories();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadLiveStreams([String categoryId = '']) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_liveStreams = await _api.getLiveStreams(categoryId);
|
||||
_selectedLiveCategory = categoryId;
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadVodStreams([String categoryId = '']) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_vodStreams = await _api.getVodStreams(categoryId);
|
||||
_selectedVodCategory = categoryId;
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadSeries() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_seriesList = await _api.getSeries();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadSeriesEpisodes(XtreamSeries series) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_selectedSeries = series;
|
||||
_seriesEpisodes = await _api.getSeriesEpisodes(series.seriesId);
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _saveCredentials(String server, String username, String password) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('server', server);
|
||||
await prefs.setString('username', username);
|
||||
await prefs.setString('password', password);
|
||||
}
|
||||
|
||||
Future<bool> loadSavedCredentials() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final server = prefs.getString('server');
|
||||
final username = prefs.getString('username');
|
||||
final password = prefs.getString('password');
|
||||
|
||||
if (server != null && username != null && password != null) {
|
||||
await login(server, username, password);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('server');
|
||||
await prefs.remove('username');
|
||||
await prefs.remove('password');
|
||||
|
||||
_userInfo = null;
|
||||
_liveCategories = [];
|
||||
_vodCategories = [];
|
||||
_seriesCategories = [];
|
||||
_liveStreams = [];
|
||||
_vodStreams = [];
|
||||
_seriesList = [];
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
161
lib/services/xtream_api.dart
Normal file
161
lib/services/xtream_api.dart
Normal file
@@ -0,0 +1,161 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../models/xtream_models.dart';
|
||||
|
||||
class XtreamApiService {
|
||||
String? _server;
|
||||
String? _username;
|
||||
String? _password;
|
||||
String? _baseUrl;
|
||||
|
||||
void setCredentials(String server, String username, String password) {
|
||||
_server = server;
|
||||
_username = username;
|
||||
_password = password;
|
||||
_baseUrl = server.startsWith('http') ? server : 'http://$server';
|
||||
}
|
||||
|
||||
String? get server => _server;
|
||||
String? get username => _username;
|
||||
|
||||
Future<Map<String, dynamic>> authenticate() async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
final response = await http.get(
|
||||
Uri.parse('$url?username=$_username&password=$_password'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return json.decode(response.body);
|
||||
}
|
||||
throw Exception('Authentication failed: ${response.statusCode}');
|
||||
}
|
||||
|
||||
Future<XtreamUserInfo> getUserInfo() async {
|
||||
final data = await authenticate();
|
||||
return XtreamUserInfo.fromJson(data);
|
||||
}
|
||||
|
||||
Future<List<XtreamCategory>> getLiveCategories() async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
final response = await http.get(
|
||||
Uri.parse('$url?username=$_username&password=$_password&action=get_live_categories'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((e) => XtreamCategory.fromJson(e)).toList();
|
||||
}
|
||||
throw Exception('Failed to load categories');
|
||||
}
|
||||
|
||||
Future<List<XtreamCategory>> getVodCategories() async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
final response = await http.get(
|
||||
Uri.parse('$url?username=$_username&password=$_password&action=get_vod_categories'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((e) => XtreamCategory.fromJson(e)).toList();
|
||||
}
|
||||
throw Exception('Failed to load VOD categories');
|
||||
}
|
||||
|
||||
Future<List<XtreamCategory>> getSeriesCategories() async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
final response = await http.get(
|
||||
Uri.parse('$url?username=$_username&password=$_password&action=get_series_categories'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((e) => XtreamCategory.fromJson(e)).toList();
|
||||
}
|
||||
throw Exception('Failed to load series categories');
|
||||
}
|
||||
|
||||
Future<List<XtreamStream>> getLiveStreams(String categoryId) async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
String apiUrl = '$url?username=$_username&password=$_password&action=get_live_streams';
|
||||
if (categoryId.isNotEmpty) {
|
||||
apiUrl += '&category_id=$categoryId';
|
||||
}
|
||||
|
||||
final response = await http.get(Uri.parse(apiUrl));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((e) {
|
||||
final stream = XtreamStream.fromJson(e);
|
||||
stream.url = '$_baseUrl/live/$_username/$_password/${stream.streamId}.ts';
|
||||
return stream;
|
||||
}).toList();
|
||||
}
|
||||
throw Exception('Failed to load live streams');
|
||||
}
|
||||
|
||||
Future<List<XtreamStream>> getVodStreams(String categoryId) async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
String apiUrl = '$url?username=$_username&password=$_password&action=get_vod_streams';
|
||||
if (categoryId.isNotEmpty) {
|
||||
apiUrl += '&category_id=$categoryId';
|
||||
}
|
||||
|
||||
final response = await http.get(Uri.parse(apiUrl));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((e) {
|
||||
final stream = XtreamStream.fromJson(e);
|
||||
final ext = stream.containerExtension ?? 'm3u8';
|
||||
stream.url = '$_baseUrl/vod/$_username/$_password/${stream.streamId}.$ext';
|
||||
return stream;
|
||||
}).toList();
|
||||
}
|
||||
throw Exception('Failed to load VOD streams');
|
||||
}
|
||||
|
||||
Future<List<XtreamSeries>> getSeries() async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
final response = await http.get(
|
||||
Uri.parse('$url?username=$_username&password=$_password&action=get_series'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
return data.map((e) => XtreamSeries.fromJson(e)).toList();
|
||||
}
|
||||
throw Exception('Failed to load series');
|
||||
}
|
||||
|
||||
Future<List<XtreamEpisode>> getSeriesEpisodes(int seriesId) async {
|
||||
final url = '$_baseUrl/player_api.php';
|
||||
final response = await http.get(
|
||||
Uri.parse('$url?username=$_username&password=$_password&action=get_series_info&series_id=$seriesId'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
final List<dynamic> episodesData = data['episodes'] ?? [];
|
||||
|
||||
final List<XtreamEpisode> allEpisodes = [];
|
||||
for (final seasonData in episodesData) {
|
||||
final season = seasonData['season_number'] ?? 0;
|
||||
final List<dynamic> episodes = seasonData['episodes'] ?? [];
|
||||
for (final ep in episodes) {
|
||||
final episode = XtreamEpisode.fromJson(ep);
|
||||
final ext = episode.containerExtension ?? 'm3u8';
|
||||
episode.url = '$_baseUrl/series/$_username/$_password/${episode.episodeId}.$ext';
|
||||
allEpisodes.add(episode);
|
||||
}
|
||||
}
|
||||
return allEpisodes;
|
||||
}
|
||||
throw Exception('Failed to load series episodes');
|
||||
}
|
||||
|
||||
String getStreamUrl(int streamId, {String type = 'live'}) {
|
||||
final ext = type == 'live' ? 'ts' : 'm3u8';
|
||||
return '$_baseUrl/$type/$_username/$_password/$streamId.$ext';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user