Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,7 @@
},
"takeScreenshot": "Take screenshot",
"takeScreenshotClean": "Take screenshot (without subtitles)",
"toggleSubtitles": "Toggle subtitles",
"screenshotTaken": "Screenshot saved!",
"screenshotCleanTaken": "Screenshot without subtitles saved!",
"errorTakingScreenshot": "There was an error taking the screenshot",
Expand Down
3 changes: 3 additions & 0 deletions lib/models/settings/video_player_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum VideoHotKeys {
skipMediaSegment,
takeScreenshot,
takeScreenshotClean,
toggleSubtitles,
exit;

const VideoHotKeys();
Expand All @@ -57,6 +58,7 @@ enum VideoHotKeys {
VideoHotKeys.skipMediaSegment => context.localized.skipMediaSegment,
VideoHotKeys.takeScreenshot => context.localized.takeScreenshot,
VideoHotKeys.takeScreenshotClean => context.localized.takeScreenshotClean,
VideoHotKeys.toggleSubtitles => context.localized.toggleSubtitles,
VideoHotKeys.exit => context.localized.exit,
};
}
Expand Down Expand Up @@ -247,6 +249,7 @@ Map<VideoHotKeys, KeyCombination> get _defaultVideoHotKeys => {
VideoHotKeys.takeScreenshot => KeyCombination(key: LogicalKeyboardKey.keyG),
VideoHotKeys.takeScreenshotClean =>
KeyCombination(key: LogicalKeyboardKey.keyG, modifier: LogicalKeyboardKey.controlLeft),
VideoHotKeys.toggleSubtitles => KeyCombination(key: LogicalKeyboardKey.keyT),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silly question but why T? Youtube for instance uses the C button for "Captions"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was initially going to go with C to mirror Youtube's logic, but since the request suggested T I just went with that, we can do whatever I don't mind.

VideoHotKeys.exit => KeyCombination(key: LogicalKeyboardKey.escape),
},
};
59 changes: 59 additions & 0 deletions lib/screens/video_player/video_player_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:screen_brightness/screen_brightness.dart';

import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/media_playback_model.dart';
import 'package:fladder/models/playback/playback_model.dart';
import 'package:fladder/models/settings/video_player_settings.dart';
Expand Down Expand Up @@ -78,10 +79,13 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
double? _vDragStartValue;
double? _vDragLastValue;

int? _lastSelectedSubtitleIndex;

@override
void initState() {
super.initState();
timer.reset();
_lastSelectedSubtitleIndex = null;
}

@override
Expand Down Expand Up @@ -909,6 +913,58 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
_vDragLastValue = null;
}

void _toggleSubtitles() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure about the logic here. Does it make sense for the user to press the "toggle" button to all of a sudden ask what subtitles they want to toggle in-between?

Would it make more sense to just do nothing if the subtitles are already disabled and only allow toggling when a subtitle is enabled?

No strong opinion on my end just thinking out loud.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not against the idea, but I think this might lead users to open issues on GitHub saying that the toggle key is not working, when in fact it would be working. Then from experience I know that most people don't check previous issues before opening one which would lead to a bunch of duplicate issues regarding this.

final playbackModel = ref.read(playBackModel);
final player = ref.read(videoPlayerProvider);
final subStreams = playbackModel?.subStreams;

if (subStreams == null || subStreams.isEmpty) return;

// Filter out the "off" track (index == -1)
final availableSubtitles = subStreams.where((s) => s.index != -1).toList();
if (availableSubtitles.isEmpty) return;

final currentIndex = playbackModel?.mediaStreams?.defaultSubStreamIndex ?? -1;

// If only one subtitle track, toggle between it and off
if (availableSubtitles.length == 1) {
final singleSub = availableSubtitles.first;
final newSub = currentIndex == singleSub.index ? SubStreamModel.no() : singleSub;
_setSubtitleTrack(newSub, playbackModel, player);
return;
}

// Multiple subtitle tracks
if (_lastSelectedSubtitleIndex == null) {
// First time - show selection dialog
showSubSelection(context).then((_) {
// After dialog closes, store the selected subtitle
final newModel = ref.read(playBackModel);
final selectedIndex = newModel?.mediaStreams?.defaultSubStreamIndex;
if (selectedIndex != null && selectedIndex != -1) {
_lastSelectedSubtitleIndex = selectedIndex;
}
});
} else {
// Toggle between off and last selected
final lastSub = subStreams.firstWhere(
(s) => s.index == _lastSelectedSubtitleIndex,
orElse: () => availableSubtitles.first,
);
final newSub = currentIndex == _lastSelectedSubtitleIndex ? SubStreamModel.no() : lastSub;
_setSubtitleTrack(newSub, playbackModel, player);
}
}

void _setSubtitleTrack(SubStreamModel subModel, PlaybackModel? playbackModel, dynamic player) async {
if (playbackModel == null) return;
final newModel = await playbackModel.setSubtitle(subModel, player);
ref.read(playBackModel.notifier).update((state) => newModel);
if (newModel != null) {
await ref.read(playbackModelHelper).shouldReload(newModel);
}
}

bool _onKey(VideoHotKeys value) {
final mediaSegments = ref.read(playBackModel.select((value) => value?.mediaSegments));
final position = ref.read(mediaPlaybackProvider).position;
Expand Down Expand Up @@ -974,6 +1030,9 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
case VideoHotKeys.prevChapter:
ref.read(videoPlayerSettingsProvider.notifier).prevChapter();
return true;
case VideoHotKeys.toggleSubtitles:
_toggleSubtitles();
return true;
case VideoHotKeys.stepForward:
playing ? ref.read(videoPlayerProvider).playOrPause() : stepForward(ref);
return true;
Expand Down
Loading