mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
feat: Add initial IPTV player with web-based playback and streaming API.
This commit is contained in:
1 parent
e3f5bc6e66
commit
f290f5a7fc
7 files changed
+354
-1440
No files matched your search
@@ -62,7 +62,6 @@ class _SettingsKeys {
|
||||
static const String showClock = 'show_clock';
|
||||
static const String preferredAspectRatio = 'aspect_ratio';
|
||||
static const String playerType = 'player_type';
|
||||
static const String forceVodTranscoding = 'force_vod_transcoding';
|
||||
}
|
||||
|
||||
/// Settings state for IPTV preferences with persistence
|
||||
@@ -86,8 +85,6 @@ class IptvSettings {
|
||||
final String preferredAspectRatio;
|
||||
final PlayerType playerType;
|
||||
|
||||
// VOD Settings
|
||||
final bool forceVodTranscoding;
|
||||
|
||||
const IptvSettings({
|
||||
// Filters
|
||||
@@ -106,8 +103,6 @@ class IptvSettings {
|
||||
this.showClock = false,
|
||||
this.preferredAspectRatio = 'contain',
|
||||
this.playerType = PlayerType.standard,
|
||||
// VOD defaults - true by default to avoid 509 errors from IPTV servers
|
||||
this.forceVodTranscoding = true,
|
||||
});
|
||||
|
||||
IptvSettings copyWith({
|
||||
@@ -124,7 +119,6 @@ class IptvSettings {
|
||||
bool? showClock,
|
||||
String? preferredAspectRatio,
|
||||
PlayerType? playerType,
|
||||
bool? forceVodTranscoding,
|
||||
}) {
|
||||
return IptvSettings(
|
||||
liveTvCategoryFilter: liveTvCategoryFilter ?? this.liveTvCategoryFilter,
|
||||
@@ -140,7 +134,6 @@ class IptvSettings {
|
||||
showClock: showClock ?? this.showClock,
|
||||
preferredAspectRatio: preferredAspectRatio ?? this.preferredAspectRatio,
|
||||
playerType: playerType ?? this.playerType,
|
||||
forceVodTranscoding: forceVodTranscoding ?? this.forceVodTranscoding,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -337,7 +330,7 @@ class IptvSettingsNotifier extends StateNotifier<IptvSettings> {
|
||||
_SettingsKeys.showClock: state.showClock,
|
||||
_SettingsKeys.preferredAspectRatio: state.preferredAspectRatio,
|
||||
_SettingsKeys.playerType: state.playerType.index,
|
||||
_SettingsKeys.forceVodTranscoding: state.forceVodTranscoding,
|
||||
_SettingsKeys.playerType: state.playerType.index,
|
||||
};
|
||||
|
||||
print('[SettingsProvider] _saveToApi: Saving settings...');
|
||||
@@ -432,12 +425,6 @@ class IptvSettingsNotifier extends StateNotifier<IptvSettings> {
|
||||
// Legacy methods
|
||||
void setCategoryFilter(String filter) => setLiveTvFilter(filter);
|
||||
void clearCategoryFilter() => clearLiveTvFilter();
|
||||
|
||||
// VOD Settings
|
||||
void setForceVodTranscoding(bool value) {
|
||||
state = state.copyWith(forceVodTranscoding: value);
|
||||
_saveToApi();
|
||||
}
|
||||
}
|
||||
|
||||
/// Provider for IPTV settings
|
||||
|
||||
@@ -99,20 +99,18 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
|
||||
|
||||
_viewId = 'iptv-player-$currentStreamId-${DateTime.now().millisecondsSinceEpoch}';
|
||||
|
||||
|
||||
final service = ref.read(xtreamServiceProvider(widget.playlist));
|
||||
String streamUrl = '';
|
||||
|
||||
// Get settings for VOD transcoding preference
|
||||
final playerSettings = ref.read(iptvSettingsProvider);
|
||||
final forceTranscoding = playerSettings.forceVodTranscoding;
|
||||
|
||||
// Generate Stream URL based on type
|
||||
// NOTE: Logic is now handled by the backend routes /api/live/ and /api/vod/
|
||||
if (widget.streamType == StreamType.live) {
|
||||
streamUrl = service.getLiveStreamUrl(currentStreamId);
|
||||
} else if (widget.streamType == StreamType.vod) {
|
||||
streamUrl = service.getVodStreamUrl(currentStreamId, widget.containerExtension, forceTranscoding: forceTranscoding);
|
||||
streamUrl = service.getVodStreamUrl(currentStreamId, widget.containerExtension);
|
||||
} else if (widget.streamType == StreamType.series) {
|
||||
streamUrl = service.getSeriesStreamUrl(currentStreamId, widget.containerExtension, forceTranscoding: forceTranscoding);
|
||||
streamUrl = service.getSeriesStreamUrl(currentStreamId, widget.containerExtension);
|
||||
}
|
||||
|
||||
// Store URL for Lite Player
|
||||
|
||||
@@ -58,57 +58,30 @@ class XtreamService {
|
||||
}
|
||||
|
||||
/// Generate stream URL for live TV
|
||||
///
|
||||
/// Returns the proxy URL for direct MPEG-TS streaming.
|
||||
/// Player uses mpegts.js for playback.
|
||||
String getLiveStreamUrl(String streamId) {
|
||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||
|
||||
// Direct .ts URL for MPEG-TS streaming
|
||||
final iptvUrl = '${_currentPlaylist!.dns}/live/${_currentPlaylist!.username}/${_currentPlaylist!.password}/$streamId.ts';
|
||||
|
||||
// Use proxy for direct streaming
|
||||
return _wrapWithProxy(iptvUrl);
|
||||
// Point to new dedicated Live API
|
||||
final baseUrl = html.window.location.origin;
|
||||
return '$baseUrl/api/live/$streamId.ts';
|
||||
}
|
||||
|
||||
/// Generate stream URL for VOD (movies)
|
||||
///
|
||||
/// [forceTranscoding] - If true, uses FFmpeg to transcode audio (guaranteed sound)
|
||||
/// If false, uses direct URL (enables seeking but may lack audio on AC3/DTS)
|
||||
String getVodStreamUrl(String streamId, String containerExtension, {bool forceTranscoding = false}) {
|
||||
String getVodStreamUrl(String streamId, String containerExtension) {
|
||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||
|
||||
final url = '${_currentPlaylist!.dns}/movie/${_currentPlaylist!.username}/${_currentPlaylist!.password}/$streamId.$containerExtension';
|
||||
|
||||
if (forceTranscoding) {
|
||||
// Use FFmpeg transcoding for guaranteed audio (no seeking)
|
||||
final baseUrl = html.window.location.origin;
|
||||
final encodedUrl = Uri.encodeComponent(url);
|
||||
return '$baseUrl/api/stream/$streamId?url=$encodedUrl';
|
||||
} else {
|
||||
// Direct URL for seeking (audio only if AAC/MP3)
|
||||
return _wrapWithProxy(url);
|
||||
}
|
||||
// Point to new dedicated VOD API (HLS playlist)
|
||||
final baseUrl = html.window.location.origin;
|
||||
return '$baseUrl/api/vod/$streamId/playlist.m3u8';
|
||||
}
|
||||
|
||||
/// Generate stream URL for series episodes
|
||||
///
|
||||
/// [forceTranscoding] - If true, uses FFmpeg to transcode audio (guaranteed sound)
|
||||
/// If false, uses direct URL (enables seeking but may lack audio on AC3/DTS)
|
||||
String getSeriesStreamUrl(String streamId, String containerExtension, {bool forceTranscoding = false}) {
|
||||
String getSeriesStreamUrl(String streamId, String containerExtension) {
|
||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||
|
||||
final url = '${_currentPlaylist!.dns}/series/${_currentPlaylist!.username}/${_currentPlaylist!.password}/$streamId.$containerExtension';
|
||||
|
||||
if (forceTranscoding) {
|
||||
// Use FFmpeg transcoding for guaranteed audio (no seeking)
|
||||
final baseUrl = html.window.location.origin;
|
||||
final encodedUrl = Uri.encodeComponent(url);
|
||||
return '$baseUrl/api/stream/$streamId?url=$encodedUrl';
|
||||
} else {
|
||||
// Direct URL for seeking (audio only if AAC/MP3)
|
||||
return _wrapWithProxy(url);
|
||||
}
|
||||
// Point to new dedicated VOD API (HLS playlist)
|
||||
final baseUrl = html.window.location.origin;
|
||||
return '$baseUrl/api/vod/$streamId/playlist.m3u8';
|
||||
}
|
||||
|
||||
/// Authenticate and get server info
|
||||
|
||||
Reference in new issue
Block a user