mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-20 05:28:05 +02:00
Films : demander le fichier sous sa vraie extension
Le serveur réclamait toujours <id>.mkv au panneau. Un film stocké en .mp4 était refusé (HTTP 551 constaté en prod). Le client transmet désormais container_extension (?ext=), validé côté serveur ; mkv reste le défaut pour un client qui ne l'envoie pas. Même approche que xtremobile, qui lit ces films sans souci. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6212948ea5
commit
fdf98f9659
3 files changed
+58
-7
No files matched your search
@@ -99,6 +99,16 @@ String _sanitizeQuality(String? quality) {
|
|||||||
|
|
||||||
bool _isValidStreamId(String id) => RegExp(r'^[a-zA-Z0-9_-]+$').hasMatch(id);
|
bool _isValidStreamId(String id) => RegExp(r'^[a-zA-Z0-9_-]+$').hasMatch(id);
|
||||||
|
|
||||||
|
/// Extension de conteneur d'un film/épisode, validée (elle finit dans l'URL
|
||||||
|
/// du panneau). `mkv` par défaut : c'était l'unique valeur jusqu'ici, les
|
||||||
|
/// anciens clients qui n'envoient pas `ext` gardent leur comportement.
|
||||||
|
String vodContainerExtension(String? ext) {
|
||||||
|
final value = ext?.toLowerCase();
|
||||||
|
return value != null && RegExp(r'^[a-z0-9]{2,5}$').hasMatch(value)
|
||||||
|
? value
|
||||||
|
: 'mkv';
|
||||||
|
}
|
||||||
|
|
||||||
/// Video encoding args for the requested quality (live profile).
|
/// Video encoding args for the requested quality (live profile).
|
||||||
List<String> _liveVideoArgs(String quality, bool gpu) {
|
List<String> _liveVideoArgs(String quality, bool gpu) {
|
||||||
switch (quality) {
|
switch (quality) {
|
||||||
@@ -680,8 +690,11 @@ Handler createVodStreamHandler(
|
|||||||
final contentType = request.url.queryParameters['type'] ?? 'movie';
|
final contentType = request.url.queryParameters['type'] ?? 'movie';
|
||||||
final basePath = contentType == 'series' ? 'series' : 'movie';
|
final basePath = contentType == 'series' ? 'series' : 'movie';
|
||||||
|
|
||||||
|
// Extension réelle du fichier (`container_extension` du catalogue) : le
|
||||||
|
// panneau refuse un `.mkv` imposé à un film stocké en `.mp4`.
|
||||||
final targetUrl =
|
final targetUrl =
|
||||||
'${playlist.dns}/$basePath/${playlist.username}/${playlist.password}/$streamId.mkv';
|
'${playlist.dns}/$basePath/${playlist.username}/${playlist.password}/'
|
||||||
|
'$streamId.${vodContainerExtension(request.url.queryParameters['ext'])}';
|
||||||
final useNvidiaGpu = isGpuEnabled?.call() ?? _isNvidiaGpuEnabled();
|
final useNvidiaGpu = isGpuEnabled?.call() ?? _isNvidiaGpuEnabled();
|
||||||
final sessionId = 'vod_${streamId}_$quality';
|
final sessionId = 'vod_${streamId}_$quality';
|
||||||
|
|
||||||
@@ -751,10 +764,15 @@ Handler createVodStreamHandler(
|
|||||||
(Request request, String streamId) async {
|
(Request request, String streamId) async {
|
||||||
final quality =
|
final quality =
|
||||||
_sanitizeQuality(request.url.queryParameters['quality']);
|
_sanitizeQuality(request.url.queryParameters['quality']);
|
||||||
final query = request.url.queryParameters['type'] != null
|
final params = request.url.queryParameters;
|
||||||
? '?type=${request.url.queryParameters['type']}'
|
final query = Uri(queryParameters: {
|
||||||
: '';
|
if (params['type'] != null) 'type': params['type']!,
|
||||||
return Response.found('/api/vod/$streamId/$quality/playlist.m3u8$query');
|
if (params['ext'] != null) 'ext': params['ext']!,
|
||||||
|
}).query;
|
||||||
|
return Response.found(
|
||||||
|
'/api/vod/$streamId/$quality/playlist.m3u8'
|
||||||
|
'${query.isEmpty ? '' : '?$query'}',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Route: /api/vod/{streamId}/{quality}/{segment}
|
// Route: /api/vod/{streamId}/{quality}/{segment}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:test/test.dart';
|
||||||
|
import '../api/streaming_handler.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('vodContainerExtension', () {
|
||||||
|
test('garde l\'extension réelle du catalogue', () {
|
||||||
|
expect(vodContainerExtension('mp4'), 'mp4');
|
||||||
|
expect(vodContainerExtension('MKV'), 'mkv');
|
||||||
|
expect(vodContainerExtension('avi'), 'avi');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ancien client sans ext : mkv, comme avant', () {
|
||||||
|
expect(vodContainerExtension(null), 'mkv');
|
||||||
|
expect(vodContainerExtension(''), 'mkv');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('refuse tout ce qui pourrait sortir du nom de fichier', () {
|
||||||
|
expect(vodContainerExtension('mp4/../x'), 'mkv');
|
||||||
|
expect(vodContainerExtension('mp4?a=b'), 'mkv');
|
||||||
|
expect(vodContainerExtension('toolongext'), 'mkv');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -191,7 +191,16 @@ class XtreamService {
|
|||||||
String quality = 'high',
|
String quality = 'high',
|
||||||
}) {
|
}) {
|
||||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||||
return '$_backendBaseUrl/api/vod/$streamId/$quality/playlist.m3u8';
|
// `ext` : le serveur demande le fichier au panneau sous son extension
|
||||||
|
// réelle — un `.mp4` réclamé en `.mkv` est refusé (HTTP 551).
|
||||||
|
return '$_backendBaseUrl/api/vod/$streamId/$quality/playlist.m3u8'
|
||||||
|
'${_extQuery(containerExtension, first: true)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _extQuery(String ext, {required bool first}) {
|
||||||
|
final value = ext.trim().toLowerCase();
|
||||||
|
if (value.isEmpty) return '';
|
||||||
|
return '${first ? '?' : '&'}ext=${Uri.encodeQueryComponent(value)}';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate stream URL for series episodes
|
/// Generate stream URL for series episodes
|
||||||
@@ -201,7 +210,8 @@ class XtreamService {
|
|||||||
String quality = 'high',
|
String quality = 'high',
|
||||||
}) {
|
}) {
|
||||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||||
return '$_backendBaseUrl/api/vod/$streamId/$quality/playlist.m3u8?type=series';
|
return '$_backendBaseUrl/api/vod/$streamId/$quality/playlist.m3u8?type=series'
|
||||||
|
'${_extQuery(containerExtension, first: false)}';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authenticate and get server info
|
/// Authenticate and get server info
|
||||||
|
|||||||
Reference in new issue
Block a user