refactor(tv): tune live channel inside player screen

This commit is contained in:
edde746
2026-02-12 17:47:51 +01:00
parent 4aced4d9ee
commit ca90600764
2 changed files with 41 additions and 28 deletions
+27 -2
View File
@@ -850,13 +850,36 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
if (!mounted) return;
// Live TV mode: bypass standard playback initialization
if (widget.isLive && widget.liveStreamUrl != null) {
if (widget.isLive) {
try {
_hasFirstFrame.value = false;
await player!.requestAudioFocus();
await _setLiveStreamOptions();
await player!.open(Media(widget.liveStreamUrl!, headers: const {'Accept-Language': 'en'}), play: true);
String streamUrl;
if (widget.liveStreamUrl != null) {
streamUrl = widget.liveStreamUrl!;
} else {
// Tune channel inside the player (shows loading spinner while tuning)
final channels = widget.liveChannels;
final channelIndex = _liveChannelIndex;
if (channels == null || channelIndex < 0 || channelIndex >= channels.length) {
throw Exception('No channel to tune');
}
final channel = channels[channelIndex];
final channelId = channel.identifier ?? channel.key;
final client = widget.liveClient!;
final result = await client.tuneChannel(widget.liveDvrKey!, channelId);
if (result == null) throw Exception('Failed to tune channel');
streamUrl = '${client.config.baseUrl}${result.streamPath}'
.withPlexToken(client.config.token);
_liveSessionIdentifier = result.sessionIdentifier;
_liveSessionPath = result.sessionPath;
}
await player!.open(Media(streamUrl, headers: const {'Accept-Language': 'en'}), play: true);
if (mounted) {
setState(() {
@@ -865,6 +888,8 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
_isPlayerInitialized = true;
});
}
_startLiveTimelineUpdates();
} catch (e) {
appLogger.e('Failed to start live TV playback', error: e);
if (mounted) {
+14 -26
View File
@@ -1,16 +1,17 @@
import 'package:flutter/material.dart';
import '../models/livetv_channel.dart';
import '../models/plex_metadata.dart';
import '../screens/video_player_screen.dart';
import '../services/plex_client.dart';
import '../utils/app_logger.dart';
import '../utils/plex_url_helper.dart';
import '../utils/video_player_navigation.dart';
/// Tune to a live TV channel and launch the video player.
/// Navigate to the video player for a live TV channel.
///
/// 1. Calls `tuneChannel()` to get metadata + stream path
/// 2. Navigates to the VideoPlayerScreen with `isLive: true`
/// Pushes the player screen immediately with a placeholder metadata.
/// The actual tuning (tune POST + decision GET) happens inside the player,
/// which shows a loading spinner while it works.
///
/// [channels] is the full channel list for channel up/down navigation.
Future<void> navigateToLiveTv(
@@ -21,43 +22,30 @@ Future<void> navigateToLiveTv(
List<LiveTvChannel>? channels,
}) async {
final channelId = channel.identifier ?? channel.key;
final scaffoldMessenger = ScaffoldMessenger.of(context);
final navigator = Navigator.of(context);
appLogger.d('Tuning to channel: ${channel.displayName} ($channelId)');
appLogger.d('Navigating to live channel: ${channel.displayName} ($channelId)');
final result = await client.tuneChannel(dvrKey, channelId);
if (result == null) {
appLogger.e('Failed to tune channel $channelId');
if (context.mounted) {
scaffoldMessenger.showSnackBar(
SnackBar(content: Text('Failed to tune to ${channel.displayName}')),
);
}
return;
}
final streamUrl = '${client.config.baseUrl}${result.streamPath}'.withPlexToken(client.config.token);
if (!context.mounted) return;
final placeholder = PlexMetadata(
ratingKey: channelId,
key: channelId,
type: 'clip',
title: channel.displayName,
);
final route = PageRouteBuilder<bool>(
settings: const RouteSettings(name: kVideoPlayerRouteName),
pageBuilder: (context, animation, secondaryAnimation) => VideoPlayerScreen(
metadata: result.metadata,
metadata: placeholder,
isLive: true,
liveChannelName: channel.displayName,
liveStreamUrl: streamUrl,
liveStreamUrl: null,
liveChannels: channels,
liveCurrentChannelIndex: channels?.indexWhere(
(ch) => (ch.identifier ?? ch.key) == channelId,
),
liveDvrKey: dvrKey,
liveClient: client,
liveSessionIdentifier: result.sessionIdentifier,
liveSessionPath: result.sessionPath,
),
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,