Merge pull request #70 from R0m1k3/antigravity

feat: Add IPTV player screen with FFmpeg transcoding, playback contro…
This commit is contained in:
LogiFlow authored and GitHub committed 2025-12-07 10:27:38 +01:00
commit 350b21934a
1 file changed
+64 -43
+64 -43
View File
@@ -64,7 +64,6 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
void initState() {
super.initState();
_currentIndex = widget.initialIndex;
_viewId = 'iptv-player-${widget.streamId}-${DateTime.now().millisecondsSinceEpoch}';
_initializePlayer();
_setupMessageListener();
@@ -260,12 +259,16 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
debugPrint('PlayerScreen: Player URL: $playerUrl');
// Generate a new view ID for every load to force iframe recreation
_viewId = 'iptv-player-${widget.streamId}-${DateTime.now().millisecondsSinceEpoch}';
// Register platform view factory
// ignore: undefined_prefixed_name
ui_web.platformViewRegistry.registerViewFactory(
_viewId,
(int viewId) {
final iframe = html.IFrameElement()
..id = _viewId // CRITICAL: Set ID for getElementById lookup
..src = playerUrl
..style.border = 'none'
..style.width = '100%'
@@ -293,54 +296,72 @@ class _PlayerScreenState extends ConsumerState<PlayerScreen> {
void _showSettingsDialog() {
showDialog(
context: context,
useRootNavigator: true, // Ensure it's on top
builder: (context) => PointerInterceptor(
child: AlertDialog(
backgroundColor: const Color(0xFF1E1E1E),
title: const Text('Réglages du Lecteur', style: TextStyle(color: Colors.white)),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Format d\'image', style: TextStyle(color: Colors.white70, fontSize: 13, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Wrap(
spacing: 8,
child: Theme(
data: Theme.of(context).copyWith(dialogBackgroundColor: const Color(0xFF1C1C1E)),
child: AlertDialog(
backgroundColor: const Color(0xFF1C1C1E).withOpacity(0.95),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: Colors.white.withOpacity(0.1)),
),
title: const Text('Réglages', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600)),
content: SizedBox(
width: 400,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildAspectRatioChip('Original', 'contain'),
_buildAspectRatioChip('Remplir', 'cover'),
_buildAspectRatioChip('Étirer', 'fill'),
const Text('Format d\'image', style: TextStyle(color: Colors.white54, fontSize: 13, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
_buildAspectRatioChip('Original', 'contain'),
_buildAspectRatioChip('Remplir', 'cover'),
_buildAspectRatioChip('Étirer', 'fill'),
],
),
if (_audioTracks.isNotEmpty) ...[
const SizedBox(height: 24),
const Text('Pistes Audio', style: TextStyle(color: Colors.white54, fontSize: 13, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
Container(
constraints: const BoxConstraints(maxHeight: 200),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(8),
),
child: SingleChildScrollView(
child: Column(
children: _audioTracks.map((track) {
return ListTile(
title: Text(track['label'] ?? 'Piste ${track['id']}', style: const TextStyle(color: Colors.white, fontSize: 14)),
subtitle: Text(track['lang'] ?? '', style: const TextStyle(color: Colors.white54, fontSize: 12)),
onTap: () => _setAudioTrack(track['id']),
dense: true,
leading: const Icon(Icons.audiotrack, size: 20, color: Colors.white54),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
hoverColor: Colors.white.withOpacity(0.1),
);
}).toList(),
),
),
),
],
],
),
if (_audioTracks.isNotEmpty) ...[
const SizedBox(height: 24),
const Text('Pistes Audio', style: TextStyle(color: Colors.white70, fontSize: 13, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Container(
constraints: const BoxConstraints(maxHeight: 200),
child: SingleChildScrollView(
child: Column(
children: _audioTracks.map((track) {
return ListTile(
title: Text(track['label'] ?? 'Piste ${track['id']}', style: const TextStyle(color: Colors.white)),
subtitle: Text(track['lang'] ?? '', style: const TextStyle(color: Colors.white54)),
onTap: () => _setAudioTrack(track['id']),
dense: true,
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.audiotrack, color: Colors.white54),
);
}).toList(),
),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(foregroundColor: Colors.white),
child: const Text('Fermer'),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Fermer'),
),
],
),
),
);