@@ -12,6 +12,9 @@ class PlayerAndroid extends PlayerBase {
|
||||
int? _bufferSizeBytes;
|
||||
bool _tunnelingEnabled = true;
|
||||
|
||||
/// Stored subtitle track ID when subtitles are hidden via sub-visibility.
|
||||
String? _hiddenSubtitleTrackId;
|
||||
|
||||
@override
|
||||
MethodChannel get methodChannel => _methodChannel;
|
||||
|
||||
@@ -193,6 +196,29 @@ class PlayerAndroid extends PlayerBase {
|
||||
case 'tunneled-playback':
|
||||
_tunnelingEnabled = value != 'no';
|
||||
break;
|
||||
case 'sub-visibility':
|
||||
if (value == 'no') {
|
||||
// Store current subtitle track and disable
|
||||
final current = state.track.subtitle;
|
||||
if (current != null && current.id != 'no') {
|
||||
_hiddenSubtitleTrackId = current.id;
|
||||
await selectSubtitleTrack(SubtitleTrack.off);
|
||||
}
|
||||
} else {
|
||||
// Restore previously hidden subtitle track
|
||||
final storedId = _hiddenSubtitleTrackId;
|
||||
if (storedId != null) {
|
||||
_hiddenSubtitleTrackId = null;
|
||||
final track = state.tracks.subtitle.cast<SubtitleTrack?>().firstWhere(
|
||||
(t) => t?.id == storedId,
|
||||
orElse: () => null,
|
||||
);
|
||||
if (track != null) {
|
||||
await selectSubtitleTrack(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Other properties are no-ops for ExoPlayer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,9 @@ class DesktopVideoControls extends StatefulWidget {
|
||||
/// Called to toggle ambient lighting (passed to settings sheet)
|
||||
final VoidCallback? onToggleAmbientLighting;
|
||||
|
||||
/// Whether subtitles are currently visible (false = hidden via sub-visibility toggle)
|
||||
final bool subtitlesVisible;
|
||||
|
||||
/// Whether to show the queue button
|
||||
final bool showQueueButton;
|
||||
|
||||
@@ -151,6 +154,7 @@ class DesktopVideoControls extends StatefulWidget {
|
||||
this.liveChannelName,
|
||||
this.isAmbientLightingEnabled = false,
|
||||
this.onToggleAmbientLighting,
|
||||
this.subtitlesVisible = true,
|
||||
this.showQueueButton = false,
|
||||
this.onQueueItemSelected,
|
||||
});
|
||||
@@ -697,6 +701,7 @@ class DesktopVideoControlsState extends State<DesktopVideoControls> {
|
||||
onNavigateLeft: navigateFromTrackToVolume,
|
||||
canControl: widget.canControl,
|
||||
isLive: widget.isLive,
|
||||
subtitlesVisible: widget.subtitlesVisible,
|
||||
showQueueButton: widget.showQueueButton,
|
||||
onQueueItemSelected: widget.onQueueItemSelected,
|
||||
shaderService: widget.shaderService,
|
||||
|
||||
@@ -256,6 +256,8 @@ class _PlexVideoControlsState extends State<PlexVideoControls> with WindowListen
|
||||
bool _showPerformanceOverlay = false;
|
||||
// Long-press 2x speed state
|
||||
bool _isLongPressing = false;
|
||||
// Subtitle visibility toggle state
|
||||
bool _subtitlesVisible = true;
|
||||
// Skip marker button focus node (for TV D-pad navigation)
|
||||
late final FocusNode _skipMarkerFocusNode;
|
||||
double? _rateBeforeLongPress;
|
||||
@@ -551,8 +553,28 @@ class _PlexVideoControlsState extends State<PlexVideoControls> with WindowListen
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: no-empty-block - stub for future subtitle toggle implementation
|
||||
void _toggleSubtitles() {}
|
||||
void _toggleSubtitles() {
|
||||
final currentTrack = widget.player.state.track.subtitle;
|
||||
// No-op if no subtitle track is selected
|
||||
if (currentTrack == null || currentTrack.id == 'no') return;
|
||||
|
||||
final newVisible = !_subtitlesVisible;
|
||||
widget.player.setProperty('sub-visibility', newVisible ? 'yes' : 'no');
|
||||
setState(() {
|
||||
_subtitlesVisible = newVisible;
|
||||
});
|
||||
}
|
||||
|
||||
void _onSubtitleTrackChanged(SubtitleTrack track) {
|
||||
// Reset visibility when user explicitly picks a new subtitle track
|
||||
if (track.id != 'no' && !_subtitlesVisible) {
|
||||
widget.player.setProperty('sub-visibility', 'yes');
|
||||
setState(() {
|
||||
_subtitlesVisible = true;
|
||||
});
|
||||
}
|
||||
widget.onSubtitleTrackChanged?.call(track);
|
||||
}
|
||||
|
||||
void _toggleShader() {
|
||||
final shaderService = widget.shaderService;
|
||||
@@ -982,7 +1004,8 @@ class _PlexVideoControlsState extends State<PlexVideoControls> with WindowListen
|
||||
onToggleFullscreen: _toggleFullscreen,
|
||||
onSwitchVersion: _switchMediaVersion,
|
||||
onAudioTrackChanged: widget.onAudioTrackChanged,
|
||||
onSubtitleTrackChanged: widget.onSubtitleTrackChanged,
|
||||
onSubtitleTrackChanged: _onSubtitleTrackChanged,
|
||||
subtitlesVisible: _subtitlesVisible,
|
||||
onLoadSeekTimes: () async {
|
||||
if (mounted) {
|
||||
await _loadSeekTimes();
|
||||
@@ -2009,7 +2032,8 @@ class _PlexVideoControlsState extends State<PlexVideoControls> with WindowListen
|
||||
onToggleAlwaysOnTop: Platform.isMacOS ? null : _toggleAlwaysOnTop,
|
||||
onSwitchVersion: _switchMediaVersion,
|
||||
onAudioTrackChanged: widget.onAudioTrackChanged,
|
||||
onSubtitleTrackChanged: widget.onSubtitleTrackChanged,
|
||||
onSubtitleTrackChanged: _onSubtitleTrackChanged,
|
||||
subtitlesVisible: _subtitlesVisible,
|
||||
onLoadSeekTimes: () async {
|
||||
if (mounted) {
|
||||
await _loadSeekTimes();
|
||||
|
||||
@@ -73,6 +73,9 @@ class TrackChapterControls extends StatelessWidget {
|
||||
/// Whether this is a live TV stream (hides speed settings).
|
||||
final bool isLive;
|
||||
|
||||
/// Whether subtitles are currently visible (false = hidden via sub-visibility toggle)
|
||||
final bool subtitlesVisible;
|
||||
|
||||
/// Whether to show the queue button
|
||||
final bool showQueueButton;
|
||||
|
||||
@@ -110,6 +113,7 @@ class TrackChapterControls extends StatelessWidget {
|
||||
this.onNavigateLeft,
|
||||
this.canControl = true,
|
||||
this.isLive = false,
|
||||
this.subtitlesVisible = true,
|
||||
this.showQueueButton = false,
|
||||
this.onQueueItemSelected,
|
||||
this.shaderService,
|
||||
@@ -267,10 +271,12 @@ class TrackChapterControls extends StatelessWidget {
|
||||
// Subtitles button
|
||||
if (_hasSubtitles(tracks)) {
|
||||
final currentIndex = buttonIndex;
|
||||
final hasActiveSubtitle = tracks?.track.subtitle != null && tracks!.track.subtitle!.id != 'no';
|
||||
final isHidden = hasActiveSubtitle && !subtitlesVisible;
|
||||
buttons.add(
|
||||
_buildTrackButton(
|
||||
buttonIndex: currentIndex,
|
||||
icon: Symbols.subtitles_rounded,
|
||||
icon: isHidden ? Symbols.subtitles_off_rounded : Symbols.subtitles_rounded,
|
||||
tooltip: t.videoControls.subtitlesButton,
|
||||
semanticLabel: t.videoControls.subtitlesButton,
|
||||
tracks: tracks,
|
||||
|
||||
Reference in New Issue
Block a user