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
14 changes: 13 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2570,5 +2570,17 @@
"@groupedFoldersDescription": {},
"downloadTranscoded": "Download transcoded",
"downloadOriginal": "Download original",
"today": "Today"
"today": "Today",
"dvPlayerSelectionTitle": "Dolby Vision Player",
"dvPlayerSelectionDesc": "Choose how to play Dolby Vision on Windows. Energy Player is required for native vision and high-quality audio passthrough (Atmos, 5.1).",
"dvPlayerAsk": "Always ask",
"dvPlayerDisabled": "Disabled (Internal player)",
"dvPlayerEnergyPlayer": "Energy Player (External)",
"dvPlayerDialogTitle": "Play with...",
"dvPlayerDialogDesc": "This content is in native Dolby Vision. Use Energy Player for accurate HDR colors and advanced audio support (Atmos/5.1 passthrough). Internal playback may have dull colors or limited audio codec support.",
"dvRememberChoice": "Remember my choice",
"dvPlayerNotInstalled": "Energy Player is not installed. Would you like to download it from the Microsoft Store?",
"dvPlayerGetFromStore": "Get from Store",
"dvEnableInstruction": "Configuration for native Dolby Vision and Audio:\n• Enable 'Allow Dolby Vision decoding' in Energy Player 'Video/Audio settings'.\n• Enable relevant 'Passthrough' options for Atmos/5.1 support.\n• Ensure HDR is enabled in Windows Display settings.",
"dvSyncWarning": "Note: Playback state is saved within Energy Player only and will not sync back to Fladder."
}
15 changes: 15 additions & 0 deletions lib/models/items/media_streams_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class MediaStreamsModel {

String? get mediaInfoTag => '${displayProfile?.value} ${resolution?.value}';

bool get hasDolbyVision => videoStreams.any((element) => element.isDolbyVision);

Widget? audioIcon(
BuildContext context,
Function()? onTap,
Expand Down Expand Up @@ -257,6 +259,19 @@ class VideoStreamModel extends StreamModel {
index: stream.index ?? -1,
);
}

bool get isDolbyVision {
final range = videoRangeType;
if (range == null) return false;
return range == VideoRangeType.dovi ||
range == VideoRangeType.doviwithhdr10 ||
range == VideoRangeType.doviwithhlg ||
range == VideoRangeType.doviwithsdr ||
range == VideoRangeType.doviwithel ||
range == VideoRangeType.doviwithhdr10plus ||
range == VideoRangeType.doviwithelhdr10plus;
}

String get prettyName {
return "${Resolution.fromVideoStream(this)?.value} - ${DisplayProfile.fromVideoStream(this).value} - (${codec.toUpperCase()})";
}
Expand Down
2 changes: 1 addition & 1 deletion lib/models/playback/direct_playback_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/widgets.dart' hide RepeatMode;

Check warning on line 1 in lib/models/playback/direct_playback_model.dart

View workflow job for this annotation

GitHub Actions / Linting & Formatting

The library 'package:flutter/widgets.dart' doesn't export a member with the hidden name 'RepeatMode'. (undefined_hidden_name) See https://dart.dev/diagnostic/undefined_hidden_name or https://dart.dev/lints/undefined_hidden_name

import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down
2 changes: 1 addition & 1 deletion lib/models/playback/transcode_playback_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/widgets.dart' hide RepeatMode;

Check warning on line 1 in lib/models/playback/transcode_playback_model.dart

View workflow job for this annotation

GitHub Actions / Linting & Formatting

The library 'package:flutter/widgets.dart' doesn't export a member with the hidden name 'RepeatMode'. (undefined_hidden_name) See https://dart.dev/diagnostic/undefined_hidden_name or https://dart.dev/lints/undefined_hidden_name

import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down
17 changes: 17 additions & 0 deletions lib/models/settings/video_player_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ abstract class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
@Default(2.0) double speedBoostRate,
@Default(true) bool enableDoubleTapSeek,
@Default(false) bool enableAdvancedVideoOptions,
@Default(DVPlayerChoice.ask) DVPlayerChoice dvPlayerChoice,
}) = _VideoPlayerSettingsModel;

double get volume => switch (defaultTargetPlatform) {
Expand Down Expand Up @@ -245,3 +246,19 @@ Map<VideoHotKeys, KeyCombination> get _defaultVideoHotKeys => {
VideoHotKeys.exit => KeyCombination(key: LogicalKeyboardKey.escape),
},
};

enum DVPlayerChoice {
internalPlayer,
ask,
energyPlayer;

const DVPlayerChoice();

String label(BuildContext context) {
return switch (this) {
DVPlayerChoice.internalPlayer => context.localized.dvPlayerDisabled,
DVPlayerChoice.ask => context.localized.dvPlayerAsk,
DVPlayerChoice.energyPlayer => context.localized.dvPlayerEnergyPlayer,
};
}
}
51 changes: 38 additions & 13 deletions lib/models/settings/video_player_settings.freezed.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions lib/models/settings/video_player_settings.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/providers/settings/video_player_settings_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,6 @@ class VideoPlayerSettingsProviderNotifier extends StateNotifier<VideoPlayerSetti
void setEnableDoubleTapSeek(bool value) => state = state.copyWith(enableDoubleTapSeek: value);

void setEnableAdvancedVideoOptions(bool value) => state = state.copyWith(enableAdvancedVideoOptions: value);

void setDVPlayerChoice(DVPlayerChoice value) => state = state.copyWith(dvPlayerChoice: value);
}
38 changes: 38 additions & 0 deletions lib/screens/settings/player_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,44 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
],
),
const SizedBox(height: 12),
if (Platform.isWindows)
...settingsListGroup(
context,
SettingsLabelDivider(label: context.localized.dvPlayerSelectionTitle),
[
SettingsListTile(
label: Text(context.localized.dvPlayerSelectionTitle),
subLabel: Text(context.localized.dvPlayerSelectionDesc),
trailing: EnumBox(
current: videoSettings.dvPlayerChoice.label(context),
itemBuilder: (context) => DVPlayerChoice.values
.map(
(entry) => ItemActionButton(
label: Text(entry.label(context)),
action: () => provider.setDVPlayerChoice(entry),
),
Comment on lines +149 to +156
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

The settings UI uses DVPlayerChoice.label(context) for the current selection, but those labels are currently hard-coded (not localized). After switching the enum labels to context.localized.*, this UI will correctly reflect translations.

Copilot uses AI. Check for mistakes.
)
.toList(),
),
),
AnimatedFadeSize(
child: Column(
children: [
SettingsMessageBox(
context.localized.dvEnableInstruction,
messageType: MessageType.info,
),
const SizedBox(height: 8),
SettingsMessageBox(
context.localized.dvSyncWarning,
messageType: MessageType.warning,
),
],
),
),
],
),
const SizedBox(height: 12),
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.mediaSegmentActions), [
...videoSettings.segmentSkipSettings.entries.sorted((a, b) => b.key.index.compareTo(a.key.index)).map(
(entry) => Padding(
Expand Down
Loading
Loading