mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-17 20:29:30 +02:00
feat: introduce Xtream Codes API service for IPTV content access and streaming.
This commit is contained in:
1 parent
66a020416e
commit
b0de7bce86
4 files changed
+39
-29
No files matched your search
@@ -152,18 +152,20 @@ Handler createStreamInitHandler() {
|
|||||||
final isLiveStream = iptvUrl.endsWith('.ts') || iptvUrl.contains('/live/');
|
final isLiveStream = iptvUrl.endsWith('.ts') || iptvUrl.contains('/live/');
|
||||||
|
|
||||||
if (isLiveStream) {
|
if (isLiveStream) {
|
||||||
// LIVE TV: Fast transcoding with ultrafast preset
|
// LIVE TV: Transcode to H.264 for browser compatibility
|
||||||
|
// Source may be HEVC/H.265 which browsers don't support
|
||||||
|
// Using superfast preset for minimal CPU usage
|
||||||
ffmpegArgs.addAll([
|
ffmpegArgs.addAll([
|
||||||
'-c:v', 'libx264',
|
'-c:v', 'libx264',
|
||||||
'-preset', 'ultrafast',
|
'-preset', 'superfast', // Fast encoding to prevent buffering
|
||||||
'-tune', 'zerolatency',
|
'-tune', 'zerolatency', // Minimize latency
|
||||||
'-profile:v', 'main', // Main profile for better quality than baseline
|
'-profile:v', 'baseline', // Maximum browser compatibility
|
||||||
'-level', '4.0',
|
'-level', '3.1',
|
||||||
'-pix_fmt', 'yuv420p',
|
'-pix_fmt', 'yuv420p',
|
||||||
'-g', '48', // 2s GOP at 24fps
|
'-g', '30', // Keyframe every second at 30fps
|
||||||
'-b:v', '4000k', // 4Mbps for good quality live
|
'-b:v', '2500k', // 2.5Mbps - balance quality/speed
|
||||||
'-maxrate', '4500k',
|
'-maxrate', '3000k',
|
||||||
'-bufsize', '8000k',
|
'-bufsize', '5000k',
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// VOD: Higher quality transcoding
|
// VOD: Higher quality transcoding
|
||||||
|
|||||||
@@ -60,14 +60,14 @@ class XtreamService {
|
|||||||
/// Generate stream URL for live TV
|
/// Generate stream URL for live TV
|
||||||
///
|
///
|
||||||
/// Returns the proxy URL for direct MPEG-TS streaming.
|
/// Returns the proxy URL for direct MPEG-TS streaming.
|
||||||
/// Player will use mpegts.js for playback (no server-side transcoding).
|
/// Player uses mpegts.js for playback.
|
||||||
String getLiveStreamUrl(String streamId) {
|
String getLiveStreamUrl(String streamId) {
|
||||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||||
|
|
||||||
// Direct .ts URL for MPEG-TS streaming
|
// Direct .ts URL for MPEG-TS streaming
|
||||||
final iptvUrl = '${_currentPlaylist!.dns}/live/${_currentPlaylist!.username}/${_currentPlaylist!.password}/$streamId.ts';
|
final iptvUrl = '${_currentPlaylist!.dns}/live/${_currentPlaylist!.username}/${_currentPlaylist!.password}/$streamId.ts';
|
||||||
|
|
||||||
// Use proxy for direct streaming (no FFmpeg transcoding)
|
// Use proxy for direct streaming
|
||||||
return _wrapWithProxy(iptvUrl);
|
return _wrapWithProxy(iptvUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -541,6 +541,15 @@
|
|||||||
// Determine stream type and try appropriate player
|
// Determine stream type and try appropriate player
|
||||||
const lowerUrl = streamUrl.toLowerCase();
|
const lowerUrl = streamUrl.toLowerCase();
|
||||||
const ext = urlParams.get('ext') ? urlParams.get('ext').toLowerCase() : '';
|
const ext = urlParams.get('ext') ? urlParams.get('ext').toLowerCase() : '';
|
||||||
|
const streamTypeParam = urlParams.get('type');
|
||||||
|
|
||||||
|
// VOD via proxy (/api/xtream/) - use direct playback
|
||||||
|
if (streamUrl.includes('/api/xtream/') && streamTypeParam === 'vod') {
|
||||||
|
log('VOD via proxy - using direct playback');
|
||||||
|
showLoading('Loading video...');
|
||||||
|
await playDirect(streamUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// HLS streams first
|
// HLS streams first
|
||||||
if (lowerUrl.includes('.m3u8') || lowerUrl.includes('/hls/')) {
|
if (lowerUrl.includes('.m3u8') || lowerUrl.includes('/hls/')) {
|
||||||
|
|||||||
+17
-18
@@ -252,20 +252,28 @@
|
|||||||
mpegts.getLoggingControl().enableError = true;
|
mpegts.getLoggingControl().enableError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure player differently for VOD vs Live
|
// Optimized configuration for stable Live TV playback
|
||||||
const playerConfig = {
|
const playerConfig = {
|
||||||
enableWorker: true,
|
enableWorker: true,
|
||||||
enableStashBuffer: !isLive, // Enable for VOD, disable for live
|
enableStashBuffer: true, // Enable buffering for stability
|
||||||
stashInitialSize: isLive ? 384 * 1024 : 512 * 1024,
|
stashInitialSize: 1024 * 1024, // 1MB initial buffer
|
||||||
autoCleanupSourceBuffer: true,
|
autoCleanupSourceBuffer: true,
|
||||||
fixAudioTimestampGap: isLive, // Only for live streams
|
autoCleanupMaxBackwardDuration: 60, // Keep 1 min of buffer
|
||||||
liveBufferLatencyChasing: isLive, // Disable for VOD
|
autoCleanupMinBackwardDuration: 30, // Cleanup after 30s
|
||||||
|
fixAudioTimestampGap: true, // Fix audio sync issues
|
||||||
|
liveBufferLatencyChasing: false, // Disable for stability (prevents skipping)
|
||||||
|
liveSync: false, // Don't auto-sync to edge
|
||||||
|
lazyLoad: false, // Load immediately
|
||||||
|
lazyLoadMaxDuration: 0,
|
||||||
|
lazyLoadRecoverDuration: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const player = mpegts.createPlayer({
|
const player = mpegts.createPlayer({
|
||||||
type: 'mpegts',
|
type: 'mpegts',
|
||||||
isLive: isLive, // Use parameter to enable/disable seeking
|
isLive: isLive,
|
||||||
url: url,
|
url: url,
|
||||||
|
hasAudio: true,
|
||||||
|
hasVideo: true,
|
||||||
}, playerConfig);
|
}, playerConfig);
|
||||||
|
|
||||||
player.attachMediaElement(video);
|
player.attachMediaElement(video);
|
||||||
@@ -361,20 +369,11 @@
|
|||||||
log('Stream type: ' + streamType);
|
log('Stream type: ' + streamType);
|
||||||
const lowerUrl = streamUrl.toLowerCase();
|
const lowerUrl = streamUrl.toLowerCase();
|
||||||
|
|
||||||
// LIVE TV: Simple MPEG-TS playback with mpegts.js
|
// LIVE TV: Direct MPEG-TS playback with mpegts.js
|
||||||
if (streamType === 'live') {
|
if (streamType === 'live') {
|
||||||
log('Live TV mode: Using MPEG-TS direct playback');
|
log('Live TV mode - using mpegts.js direct');
|
||||||
showLoading('Connecting to live stream...');
|
showLoading('Connecting to live stream...');
|
||||||
|
await playWithMpegTS(streamUrl, true);
|
||||||
// Check for HLS first (.m3u8)
|
|
||||||
if (lowerUrl.includes('.m3u8') || lowerUrl.includes('/hls/')) {
|
|
||||||
log('Live HLS detected');
|
|
||||||
await playWithHLS(streamUrl);
|
|
||||||
} else {
|
|
||||||
// Default: MPEG-TS with mpegts.js
|
|
||||||
log('Using mpegts.js for MPEG-TS');
|
|
||||||
await playWithMpegTS(streamUrl, true); // isLive=true
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user