Merge pull request #76 from R0m1k3/antigravity

Antigravity
This commit is contained in:
LogiFlow authored and GitHub committed 2025-12-07 15:29:18 +01:00
commit 9e6efac8e5
2 files changed
+50 -8

No files matched your search

+48 -6
View File
@@ -279,10 +279,17 @@ Future<Response> _streamVideoFile(Uri targetUrl, String? rangeHeader) async {
// Forward the Range header for seeking support // Forward the Range header for seeking support
if (rangeHeader != null && rangeHeader.isNotEmpty) { if (rangeHeader != null && rangeHeader.isNotEmpty) {
req.headers.set('Range', rangeHeader); // Ensure we request bytes
if (!rangeHeader.startsWith('bytes=')) {
rangeHeader = 'bytes=$rangeHeader';
}
req.headers.set(HttpHeaders.rangeHeader, rangeHeader);
print('Video streaming with Range: $rangeHeader'); print('Video streaming with Range: $rangeHeader');
} }
// Disable auto-decompression to strictly proxy the stream
client.autoUncompress = false;
final response = await req.close(); final response = await req.close();
// Get content type from response // Get content type from response
@@ -303,11 +310,14 @@ Future<Response> _streamVideoFile(Uri targetUrl, String? rangeHeader) async {
} }
// Add Content-Range if this is a partial response (206) // Add Content-Range if this is a partial response (206)
final contentRange = response.headers.value('content-range'); final contentRange = response.headers.value(HttpHeaders.contentRangeHeader);
if (contentRange != null) { if (contentRange != null) {
responseHeaders['content-range'] = contentRange; responseHeaders[HttpHeaders.contentRangeHeader] = contentRange;
} }
// Propagate Accept-Ranges
responseHeaders[HttpHeaders.acceptRangesHeader] = 'bytes';
// Return appropriate status code (200 for full, 206 for partial) // Return appropriate status code (200 for full, 206 for partial)
return Response( return Response(
response.statusCode, response.statusCode,
@@ -418,6 +428,8 @@ Handler _createStreamHandler() {
// Get parameters // Get parameters
final iptvUrl = request.url.queryParameters['url']; final iptvUrl = request.url.queryParameters['url'];
final quality = request.url.queryParameters['quality'] ?? 'high';
if (iptvUrl == null || iptvUrl.isEmpty) { if (iptvUrl == null || iptvUrl.isEmpty) {
return Response.badRequest(body: 'Missing url parameter'); return Response.badRequest(body: 'Missing url parameter');
} }
@@ -452,9 +464,39 @@ Handler _createStreamHandler() {
// Output format: MPEG-TS for streaming (compatible with mpegts.js) // Output format: MPEG-TS for streaming (compatible with mpegts.js)
'-f', 'mpegts', '-f', 'mpegts',
// Video: Copy is BEST for performance (0% CPU) // Video: Transcode based on quality
// Browsers support H.264 well. // High = Copy (efficient), Low/Medium = Transcode to H.264 (compatible)
'-c:v', 'copy', if (quality == 'high') {
ffmpegArgs.addAll(['-c:v', 'copy']);
} else {
// Transcode to H.264 (Baseline profile for max mobile compatibility)
ffmpegArgs.addAll([
'-c:v', 'libx264',
'-preset', 'ultrafast', // Critical for live streaming
'-tune', 'zerolatency',
'-profile:v', 'baseline',
'-level', '3.0',
'-pix_fmt', 'yuv420p', // Ensure standard pixel format for web
'-g', '60', // Keyframe interval (2s at 30fps) for seeking
]);
if (quality == 'low') {
// Low: ~480p, 800k
ffmpegArgs.addAll([
'-vf', 'scale=-2:480',
'-b:v', '800k',
'-maxrate', '800k',
'-bufsize', '1600k'
]);
} else {
// Medium: Original res (or cap at 720p?), 2500k
ffmpegArgs.addAll([
'-b:v', '2500k',
'-maxrate', '2500k',
'-bufsize', '5000k'
]);
}
}
// Audio: Transcode to AAC is SAFEST for browsers // Audio: Transcode to AAC is SAFEST for browsers
// (Many IPTV streams typically use MP2/AC3 which browsers hate) // (Many IPTV streams typically use MP2/AC3 which browsers hate)
+2 -2
View File
@@ -735,11 +735,11 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
// EPG Overlay // EPG Overlay
if (widget.streamType == StreamType.live) if (widget.streamType == StreamType.live)
Positioned( Positioned(
bottom: 0, bottom: 100, // Move up above progress bar area/OS safe area
left: 0, left: 0,
right: 0, right: 0,
child: Container( child: Container(
padding: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.only(bottom: 8),
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.bottomCenter, begin: Alignment.bottomCenter,