feat: add audio passthrough toggle for desktop

This commit is contained in:
edde746
2026-02-25 16:38:58 +01:00
parent fc5fcbb0db
commit 88d7e2d319
21 changed files with 86 additions and 9 deletions
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Untertitel-Synchronisation",
"hdr": "HDR",
"audioOutput": "Audioausgabe",
"performanceOverlay": "Leistungsanzeige"
"performanceOverlay": "Leistungsanzeige",
"audioPassthrough": "Audio-Durchleitung"
},
"externalPlayer": {
"title": "Externer Player",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Subtitle Sync",
"hdr": "HDR",
"audioOutput": "Audio Output",
"performanceOverlay": "Performance Overlay"
"performanceOverlay": "Performance Overlay",
"audioPassthrough": "Audio Passthrough"
},
"externalPlayer": {
"title": "External Player",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Sincronización de subtítulos",
"hdr": "HDR",
"audioOutput": "Salida de audio",
"performanceOverlay": "Indicador de rendimiento"
"performanceOverlay": "Indicador de rendimiento",
"audioPassthrough": "Audio Passthrough"
},
"externalPlayer": {
"title": "Reproductor externo",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Synchronisation des sous-titres",
"hdr": "HDR",
"audioOutput": "Sortie audio",
"performanceOverlay": "Superposition de performance"
"performanceOverlay": "Superposition de performance",
"audioPassthrough": "Audio Pass-Through"
},
"externalPlayer": {
"title": "Lecteur externe",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Sincronizzazione sottotitoli",
"hdr": "HDR",
"audioOutput": "Uscita audio",
"performanceOverlay": "Overlay prestazioni"
"performanceOverlay": "Overlay prestazioni",
"audioPassthrough": "Audio Passthrough"
},
"externalPlayer": {
"title": "Lettore esterno",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "자막 동기화",
"hdr": "HDR",
"audioOutput": "오디오 출력",
"performanceOverlay": "성능 오버레이"
"performanceOverlay": "성능 오버레이",
"audioPassthrough": "오디오 패스스루"
},
"externalPlayer": {
"title": "외부 플레이어",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Ondertitel synchronisatie",
"hdr": "HDR",
"audioOutput": "Audio-uitvoer",
"performanceOverlay": "Prestatie-overlay"
"performanceOverlay": "Prestatie-overlay",
"audioPassthrough": "Audio-doorvoer"
},
"externalPlayer": {
"title": "Externe speler",
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsDe implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => 'Audioausgabe';
@override String get performanceOverlay => 'Leistungsanzeige';
@override String get audioPassthrough => 'Audio-Durchleitung';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsDe {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Audioausgabe',
'videoSettings.performanceOverlay' => 'Leistungsanzeige',
'videoSettings.audioPassthrough' => 'Audio-Durchleitung',
'externalPlayer.title' => 'Externer Player',
'externalPlayer.useExternalPlayer' => 'Externen Player verwenden',
'externalPlayer.useExternalPlayerDescription' => 'Videos in einer externen App statt im integrierten Player öffnen',
+4
View File
@@ -2249,6 +2249,9 @@ class TranslationsVideoSettingsEn {
/// en: 'Performance Overlay'
String get performanceOverlay => 'Performance Overlay';
/// en: 'Audio Passthrough'
String get audioPassthrough => 'Audio Passthrough';
}
// Path: externalPlayer
@@ -3620,6 +3623,7 @@ extension on Translations {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Audio Output',
'videoSettings.performanceOverlay' => 'Performance Overlay',
'videoSettings.audioPassthrough' => 'Audio Passthrough',
'externalPlayer.title' => 'External Player',
'externalPlayer.useExternalPlayer' => 'Use External Player',
'externalPlayer.useExternalPlayerDescription' => 'Open videos in an external app instead of the built-in player',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsEs implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => 'Salida de audio';
@override String get performanceOverlay => 'Indicador de rendimiento';
@override String get audioPassthrough => 'Audio Passthrough';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsEs {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Salida de audio',
'videoSettings.performanceOverlay' => 'Indicador de rendimiento',
'videoSettings.audioPassthrough' => 'Audio Passthrough',
'externalPlayer.title' => 'Reproductor externo',
'externalPlayer.useExternalPlayer' => 'Usar reproductor externo',
'externalPlayer.useExternalPlayerDescription' => 'Abrir vídeos en una app externa en lugar del reproductor integrado',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsFr implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => 'Sortie audio';
@override String get performanceOverlay => 'Superposition de performance';
@override String get audioPassthrough => 'Audio Pass-Through';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsFr {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Sortie audio',
'videoSettings.performanceOverlay' => 'Superposition de performance',
'videoSettings.audioPassthrough' => 'Audio Pass-Through',
'externalPlayer.title' => 'Lecteur externe',
'externalPlayer.useExternalPlayer' => 'Utiliser un lecteur externe',
'externalPlayer.useExternalPlayerDescription' => 'Ouvrir les vidéos dans une application externe au lieu du lecteur intégré',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsIt implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => 'Uscita audio';
@override String get performanceOverlay => 'Overlay prestazioni';
@override String get audioPassthrough => 'Audio Passthrough';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsIt {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Uscita audio',
'videoSettings.performanceOverlay' => 'Overlay prestazioni',
'videoSettings.audioPassthrough' => 'Audio Passthrough',
'externalPlayer.title' => 'Lettore esterno',
'externalPlayer.useExternalPlayer' => 'Usa lettore esterno',
'externalPlayer.useExternalPlayerDescription' => 'Apri i video in un\'app esterna invece del lettore integrato',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsKo implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => '오디오 출력';
@override String get performanceOverlay => '성능 오버레이';
@override String get audioPassthrough => '오디오 패스스루';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsKo {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => '오디오 출력',
'videoSettings.performanceOverlay' => '성능 오버레이',
'videoSettings.audioPassthrough' => '오디오 패스스루',
'externalPlayer.title' => '외부 플레이어',
'externalPlayer.useExternalPlayer' => '외부 플레이어 사용',
'externalPlayer.useExternalPlayerDescription' => '내장 플레이어 대신 외부 앱에서 동영상 열기',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsNl implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => 'Audio-uitvoer';
@override String get performanceOverlay => 'Prestatie-overlay';
@override String get audioPassthrough => 'Audio-doorvoer';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsNl {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Audio-uitvoer',
'videoSettings.performanceOverlay' => 'Prestatie-overlay',
'videoSettings.audioPassthrough' => 'Audio-doorvoer',
'externalPlayer.title' => 'Externe speler',
'externalPlayer.useExternalPlayer' => 'Externe speler gebruiken',
'externalPlayer.useExternalPlayerDescription' => 'Open video\'s in een externe app in plaats van de ingebouwde speler',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsSv implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => 'Ljudutgång';
@override String get performanceOverlay => 'Prestandaöverlägg';
@override String get audioPassthrough => 'Ljudgenomkoppling';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsSv {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => 'Ljudutgång',
'videoSettings.performanceOverlay' => 'Prestandaöverlägg',
'videoSettings.audioPassthrough' => 'Ljudgenomkoppling',
'externalPlayer.title' => 'Extern spelare',
'externalPlayer.useExternalPlayer' => 'Använd extern spelare',
'externalPlayer.useExternalPlayerDescription' => 'Öppna videor i en extern app istället för den inbyggda spelaren',
+2
View File
@@ -1018,6 +1018,7 @@ class _TranslationsVideoSettingsZh implements TranslationsVideoSettingsEn {
@override String get hdr => 'HDR';
@override String get audioOutput => '音频输出';
@override String get performanceOverlay => '性能监控';
@override String get audioPassthrough => '音频直通';
}
// Path: externalPlayer
@@ -2019,6 +2020,7 @@ extension on TranslationsZh {
'videoSettings.hdr' => 'HDR',
'videoSettings.audioOutput' => '音频输出',
'videoSettings.performanceOverlay' => '性能监控',
'videoSettings.audioPassthrough' => '音频直通',
'externalPlayer.title' => '外部播放器',
'externalPlayer.useExternalPlayer' => '使用外部播放器',
'externalPlayer.useExternalPlayerDescription' => '在外部应用中打开视频,而不是使用内置播放器',
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "Undertextsynkronisering",
"hdr": "HDR",
"audioOutput": "Ljudutgång",
"performanceOverlay": "Prestandaöverlägg"
"performanceOverlay": "Prestandaöverlägg",
"audioPassthrough": "Ljudgenomkoppling"
},
"externalPlayer": {
"title": "Extern spelare",
+2 -1
View File
@@ -806,7 +806,8 @@
"subtitleSync": "字幕同步",
"hdr": "HDR",
"audioOutput": "音频输出",
"performanceOverlay": "性能监控"
"performanceOverlay": "性能监控",
"audioPassthrough": "音频直通"
},
"externalPlayer": {
"title": "外部播放器",
+7
View File
@@ -433,6 +433,13 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
await player!.setProperty('audio-exclusive', 'yes');
}
// Audio passthrough (desktop only - sends bitstream to receiver)
if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
if (settingsService.getAudioPassthrough()) {
await player!.setAudioPassthrough(true);
}
}
// HDR is controlled via custom hdr-enabled property on iOS/macOS/Windows
if (Platform.isIOS || Platform.isMacOS || Platform.isWindows) {
final enableHDR = settingsService.getEnableHDR();
+11
View File
@@ -77,6 +77,7 @@ class SettingsService extends BaseSharedPreferencesService {
static const String _keyCustomExternalPlayers = 'custom_external_players';
static const String _keyConfirmExitOnBack = 'confirm_exit_on_back';
static const String _keyAmbientLighting = 'ambient_lighting';
static const String _keyAudioPassthrough = 'audio_passthrough';
SettingsService._();
@@ -1129,6 +1130,15 @@ class SettingsService extends BaseSharedPreferencesService {
return prefs.getBool(_keyAmbientLighting) ?? false;
}
// Audio Passthrough
Future<void> setAudioPassthrough(bool enabled) async {
await prefs.setBool(_keyAudioPassthrough, enabled);
}
bool getAudioPassthrough() {
return prefs.getBool(_keyAudioPassthrough) ?? false;
}
// Reset all settings to defaults
Future<void> resetAllSettings() async {
await Future.wait([
@@ -1184,6 +1194,7 @@ class SettingsService extends BaseSharedPreferencesService {
prefs.remove(_keyCustomExternalPlayers),
prefs.remove(_keyConfirmExitOnBack),
prefs.remove(_keyAmbientLighting),
prefs.remove(_keyAudioPassthrough),
prefs.remove(_keyBufferSizeMigratedToAuto),
]);
}
@@ -115,6 +115,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
bool _enableHDR = true;
bool _showPerformanceOverlay = false;
bool _autoPlayNextEpisode = true;
bool _audioPassthrough = false;
@override
void initState() {
@@ -131,6 +132,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
_enableHDR = settings.getEnableHDR();
_showPerformanceOverlay = settings.getShowPerformanceOverlay();
_autoPlayNextEpisode = settings.getAutoPlayNextEpisode();
_audioPassthrough = settings.getAudioPassthrough();
});
}
@@ -166,6 +168,17 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
});
}
Future<void> _toggleAudioPassthrough() async {
final newValue = !_audioPassthrough;
final settings = await SettingsService.getInstance();
await settings.setAudioPassthrough(newValue);
if (!mounted) return;
setState(() {
_audioPassthrough = newValue;
});
await widget.player.setAudioPassthrough(newValue);
}
void _navigateTo(_SettingsView view) {
setState(() {
_currentView = view;
@@ -331,6 +344,23 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
},
),
// Audio Passthrough (Desktop only)
if (isDesktop)
ListTile(
leading: AppIcon(
Symbols.surround_sound_rounded,
fill: 1,
color: _audioPassthrough ? Colors.amber : tokens(context).textMuted,
),
title: Text(t.videoSettings.audioPassthrough),
trailing: Switch(
value: _audioPassthrough,
onChanged: (_) => _toggleAudioPassthrough(),
activeThumbColor: Colors.amber,
),
onTap: _toggleAudioPassthrough,
),
// Shader Preset (MPV only)
if (widget.shaderService != null && widget.shaderService!.isSupported)
_SettingsMenuItem(