+110
-2
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 7
|
||||
/// Strings: 3505 (500 per locale)
|
||||
/// Strings: 3533 (504 per locale)
|
||||
///
|
||||
/// Built on 2025-12-21 at 14:43 UTC
|
||||
/// Built on 2025-12-23 at 06:54 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
@@ -398,6 +398,10 @@ class _StringsSettingsEn {
|
||||
String get downloadOnWifiOnlyDescription => 'Prevent downloads when on cellular data';
|
||||
String get cellularDownloadBlocked =>
|
||||
'Downloads are disabled on cellular data. Connect to WiFi or change the setting.';
|
||||
String get maxVolume => 'Maximum Volume';
|
||||
String get maxVolumeDescription => 'Allow volume boost above 100% for quiet media';
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
String get maxVolumeHint => 'Enter max volume (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -1450,6 +1454,14 @@ class _StringsSettingsDe implements _StringsSettingsEn {
|
||||
@override
|
||||
String get cellularDownloadBlocked =>
|
||||
'Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung.';
|
||||
@override
|
||||
String get maxVolume => 'Maximale Lautstärke';
|
||||
@override
|
||||
String get maxVolumeDescription => 'Lautstärke über 100% für leise Medien erlauben';
|
||||
@override
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
@override
|
||||
String get maxVolumeHint => 'Maximale Lautstärke eingeben (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -2902,6 +2914,14 @@ class _StringsSettingsIt implements _StringsSettingsEn {
|
||||
@override
|
||||
String get cellularDownloadBlocked =>
|
||||
'I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l\'impostazione.';
|
||||
@override
|
||||
String get maxVolume => 'Volume massimo';
|
||||
@override
|
||||
String get maxVolumeDescription => 'Consenti volume superiore al 100% per contenuti audio bassi';
|
||||
@override
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
@override
|
||||
String get maxVolumeHint => 'Inserisci volume massimo (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -4347,6 +4367,14 @@ class _StringsSettingsKo implements _StringsSettingsEn {
|
||||
String get downloadOnWifiOnlyDescription => '셀룰러 데이터 사용 시 다운로드 불가';
|
||||
@override
|
||||
String get cellularDownloadBlocked => '셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요.';
|
||||
@override
|
||||
String get maxVolume => '최대 볼륨';
|
||||
@override
|
||||
String get maxVolumeDescription => '조용한 미디어를 위해 100% 이상의 볼륨 허용';
|
||||
@override
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
@override
|
||||
String get maxVolumeHint => '최대 볼륨 입력 (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -5795,6 +5823,14 @@ class _StringsSettingsNl implements _StringsSettingsEn {
|
||||
@override
|
||||
String get cellularDownloadBlocked =>
|
||||
'Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling.';
|
||||
@override
|
||||
String get maxVolume => 'Maximaal volume';
|
||||
@override
|
||||
String get maxVolumeDescription => 'Volume boven 100% toestaan voor stille media';
|
||||
@override
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
@override
|
||||
String get maxVolumeHint => 'Voer maximaal volume in (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -7246,6 +7282,14 @@ class _StringsSettingsSv implements _StringsSettingsEn {
|
||||
@override
|
||||
String get cellularDownloadBlocked =>
|
||||
'Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen.';
|
||||
@override
|
||||
String get maxVolume => 'Maximal volym';
|
||||
@override
|
||||
String get maxVolumeDescription => 'Tillåt volym över 100% för tyst media';
|
||||
@override
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
@override
|
||||
String get maxVolumeHint => 'Ange maximal volym (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -8693,6 +8737,14 @@ class _StringsSettingsZh implements _StringsSettingsEn {
|
||||
String get downloadOnWifiOnlyDescription => '使用蜂窝数据时禁止下载';
|
||||
@override
|
||||
String get cellularDownloadBlocked => '蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。';
|
||||
@override
|
||||
String get maxVolume => '最大音量';
|
||||
@override
|
||||
String get maxVolumeDescription => '允许音量超过 100% 以适应安静的媒体';
|
||||
@override
|
||||
String maxVolumePercent({required Object percent}) => '${percent}%';
|
||||
@override
|
||||
String get maxVolumeHint => '输入最大音量 (100-300)';
|
||||
}
|
||||
|
||||
// Path: search
|
||||
@@ -9988,6 +10040,14 @@ extension on Translations {
|
||||
return 'Prevent downloads when on cellular data';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return 'Downloads are disabled on cellular data. Connect to WiFi or change the setting.';
|
||||
case 'settings.maxVolume':
|
||||
return 'Maximum Volume';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return 'Allow volume boost above 100% for quiet media';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return 'Enter max volume (100-300)';
|
||||
case 'search.hint':
|
||||
return 'Search movies, shows, music...';
|
||||
case 'search.tryDifferentTerm':
|
||||
@@ -11000,6 +11060,14 @@ extension on _StringsDe {
|
||||
return 'Downloads über mobile Daten verhindern';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return 'Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung.';
|
||||
case 'settings.maxVolume':
|
||||
return 'Maximale Lautstärke';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return 'Lautstärke über 100% für leise Medien erlauben';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return 'Maximale Lautstärke eingeben (100-300)';
|
||||
case 'search.hint':
|
||||
return 'Filme, Serien, Musik suchen...';
|
||||
case 'search.tryDifferentTerm':
|
||||
@@ -12016,6 +12084,14 @@ extension on _StringsIt {
|
||||
return 'Impedisci i download quando si utilizza la rete dati cellulare';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return 'I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l\'impostazione.';
|
||||
case 'settings.maxVolume':
|
||||
return 'Volume massimo';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return 'Consenti volume superiore al 100% per contenuti audio bassi';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return 'Inserisci volume massimo (100-300)';
|
||||
case 'search.hint':
|
||||
return 'Cerca film. spettacoli, musica...';
|
||||
case 'search.tryDifferentTerm':
|
||||
@@ -13032,6 +13108,14 @@ extension on _StringsKo {
|
||||
return '셀룰러 데이터 사용 시 다운로드 불가';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return '셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요.';
|
||||
case 'settings.maxVolume':
|
||||
return '최대 볼륨';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return '조용한 미디어를 위해 100% 이상의 볼륨 허용';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return '최대 볼륨 입력 (100-300)';
|
||||
case 'search.hint':
|
||||
return '영화, 시리즈, 음악 등을 검색하세요...';
|
||||
case 'search.tryDifferentTerm':
|
||||
@@ -14045,6 +14129,14 @@ extension on _StringsNl {
|
||||
return 'Voorkom downloads bij gebruik van mobiele data';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return 'Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling.';
|
||||
case 'settings.maxVolume':
|
||||
return 'Maximaal volume';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return 'Volume boven 100% toestaan voor stille media';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return 'Voer maximaal volume in (100-300)';
|
||||
case 'search.hint':
|
||||
return 'Zoek films, series, muziek...';
|
||||
case 'search.tryDifferentTerm':
|
||||
@@ -15061,6 +15153,14 @@ extension on _StringsSv {
|
||||
return 'Förhindra nedladdningar vid användning av mobildata';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return 'Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen.';
|
||||
case 'settings.maxVolume':
|
||||
return 'Maximal volym';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return 'Tillåt volym över 100% för tyst media';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return 'Ange maximal volym (100-300)';
|
||||
case 'search.hint':
|
||||
return 'Sök filmer, serier, musik...';
|
||||
case 'search.tryDifferentTerm':
|
||||
@@ -16076,6 +16176,14 @@ extension on _StringsZh {
|
||||
return '使用蜂窝数据时禁止下载';
|
||||
case 'settings.cellularDownloadBlocked':
|
||||
return '蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。';
|
||||
case 'settings.maxVolume':
|
||||
return '最大音量';
|
||||
case 'settings.maxVolumeDescription':
|
||||
return '允许音量超过 100% 以适应安静的媒体';
|
||||
case 'settings.maxVolumePercent':
|
||||
return ({required Object percent}) => '${percent}%';
|
||||
case 'settings.maxVolumeHint':
|
||||
return '输入最大音量 (100-300)';
|
||||
case 'search.hint':
|
||||
return '搜索电影、系列、音乐...';
|
||||
case 'search.tryDifferentTerm':
|
||||
|
||||
@@ -142,7 +142,11 @@
|
||||
"downloadLocationSelectError": "Failed to select folder",
|
||||
"downloadOnWifiOnly": "Download on WiFi only",
|
||||
"downloadOnWifiOnlyDescription": "Prevent downloads when on cellular data",
|
||||
"cellularDownloadBlocked": "Downloads are disabled on cellular data. Connect to WiFi or change the setting."
|
||||
"cellularDownloadBlocked": "Downloads are disabled on cellular data. Connect to WiFi or change the setting.",
|
||||
"maxVolume": "Maximum Volume",
|
||||
"maxVolumeDescription": "Allow volume boost above 100% for quiet media",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "Enter max volume (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "Search movies, shows, music...",
|
||||
|
||||
@@ -143,7 +143,11 @@
|
||||
"downloadLocationSelectError": "Ordnerauswahl fehlgeschlagen",
|
||||
"downloadOnWifiOnly": "Nur über WLAN herunterladen",
|
||||
"downloadOnWifiOnlyDescription": "Downloads über mobile Daten verhindern",
|
||||
"cellularDownloadBlocked": "Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung."
|
||||
"cellularDownloadBlocked": "Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung.",
|
||||
"maxVolume": "Maximale Lautstärke",
|
||||
"maxVolumeDescription": "Lautstärke über 100% für leise Medien erlauben",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "Maximale Lautstärke eingeben (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "Filme, Serien, Musik suchen...",
|
||||
|
||||
@@ -143,7 +143,11 @@
|
||||
"downloadLocationSelectError": "Impossibile selezionare la cartella",
|
||||
"downloadOnWifiOnly": "Scarica solo con WiFi",
|
||||
"downloadOnWifiOnlyDescription": "Impedisci i download quando si utilizza la rete dati cellulare",
|
||||
"cellularDownloadBlocked": "I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l'impostazione."
|
||||
"cellularDownloadBlocked": "I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l'impostazione.",
|
||||
"maxVolume": "Volume massimo",
|
||||
"maxVolumeDescription": "Consenti volume superiore al 100% per contenuti audio bassi",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "Inserisci volume massimo (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "Cerca film. spettacoli, musica...",
|
||||
|
||||
@@ -143,7 +143,11 @@
|
||||
"downloadLocationSelectError": "폴더 선택 실패",
|
||||
"downloadOnWifiOnly": "WiFi 연결 시에만 다운로드",
|
||||
"downloadOnWifiOnlyDescription": "셀룰러 데이터 사용 시 다운로드 불가",
|
||||
"cellularDownloadBlocked": "셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요."
|
||||
"cellularDownloadBlocked": "셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요.",
|
||||
"maxVolume": "최대 볼륨",
|
||||
"maxVolumeDescription": "조용한 미디어를 위해 100% 이상의 볼륨 허용",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "최대 볼륨 입력 (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "영화, 시리즈, 음악 등을 검색하세요...",
|
||||
|
||||
@@ -143,7 +143,11 @@
|
||||
"downloadLocationSelectError": "Kan map niet selecteren",
|
||||
"downloadOnWifiOnly": "Alleen via WiFi downloaden",
|
||||
"downloadOnWifiOnlyDescription": "Voorkom downloads bij gebruik van mobiele data",
|
||||
"cellularDownloadBlocked": "Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling."
|
||||
"cellularDownloadBlocked": "Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling.",
|
||||
"maxVolume": "Maximaal volume",
|
||||
"maxVolumeDescription": "Volume boven 100% toestaan voor stille media",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "Voer maximaal volume in (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "Zoek films, series, muziek...",
|
||||
|
||||
@@ -143,7 +143,11 @@
|
||||
"downloadLocationSelectError": "Kunde inte välja mapp",
|
||||
"downloadOnWifiOnly": "Ladda ner endast på WiFi",
|
||||
"downloadOnWifiOnlyDescription": "Förhindra nedladdningar vid användning av mobildata",
|
||||
"cellularDownloadBlocked": "Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen."
|
||||
"cellularDownloadBlocked": "Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen.",
|
||||
"maxVolume": "Maximal volym",
|
||||
"maxVolumeDescription": "Tillåt volym över 100% för tyst media",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "Ange maximal volym (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "Sök filmer, serier, musik...",
|
||||
|
||||
@@ -143,7 +143,11 @@
|
||||
"downloadLocationSelectError": "选择文件夹失败",
|
||||
"downloadOnWifiOnly": "仅在 WiFi 时下载",
|
||||
"downloadOnWifiOnlyDescription": "使用蜂窝数据时禁止下载",
|
||||
"cellularDownloadBlocked": "蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。"
|
||||
"cellularDownloadBlocked": "蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。",
|
||||
"maxVolume": "最大音量",
|
||||
"maxVolumeDescription": "允许音量超过 100% 以适应安静的媒体",
|
||||
"maxVolumePercent": "${percent}%",
|
||||
"maxVolumeHint": "输入最大音量 (100-300)"
|
||||
},
|
||||
"search": {
|
||||
"hint": "搜索电影、系列、音乐...",
|
||||
|
||||
@@ -1064,9 +1064,7 @@ class _DiscoverScreenState extends State<DiscoverScreen>
|
||||
Widget _buildHeroSection() {
|
||||
final statusBarHeight = MediaQuery.of(context).padding.top;
|
||||
final useSideNav = PlatformDetector.shouldUseSideNavigation(context);
|
||||
final heroHeight = useSideNav
|
||||
? MediaQuery.of(context).size.height * 0.75
|
||||
: 500 + statusBarHeight;
|
||||
final heroHeight = useSideNav ? MediaQuery.of(context).size.height * 0.75 : 500 + statusBarHeight;
|
||||
return SliverToBoxAdapter(
|
||||
child: Focus(
|
||||
focusNode: _heroFocusNode,
|
||||
|
||||
@@ -60,6 +60,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
int _autoSkipDelay = 5;
|
||||
bool _downloadOnWifiOnly = false;
|
||||
bool _videoPlayerNavigationEnabled = false;
|
||||
int _maxVolume = 100;
|
||||
|
||||
// Update checking state
|
||||
bool _isCheckingForUpdate = false;
|
||||
@@ -90,6 +91,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
_autoSkipDelay = _settingsService.getAutoSkipDelay();
|
||||
_downloadOnWifiOnly = _settingsService.getDownloadOnWifiOnly();
|
||||
_videoPlayerNavigationEnabled = _settingsService.getVideoPlayerNavigationEnabled();
|
||||
_maxVolume = _settingsService.getMaxVolume();
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -283,6 +285,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
|
||||
onTap: () => _showSleepTimerDurationDialog(),
|
||||
),
|
||||
ListTile(
|
||||
leading: const AppIcon(Symbols.volume_up_rounded, fill: 1),
|
||||
title: Text(t.settings.maxVolume),
|
||||
subtitle: Text(t.settings.maxVolumePercent(percent: _maxVolume.toString())),
|
||||
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
|
||||
onTap: () => _showMaxVolumeDialog(),
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const AppIcon(Symbols.bookmark_rounded, fill: 1),
|
||||
title: Text(t.settings.rememberTrackSelections),
|
||||
@@ -966,6 +975,21 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showMaxVolumeDialog() {
|
||||
_showNumericInputDialog(
|
||||
title: t.settings.maxVolume,
|
||||
labelText: t.settings.maxVolumeDescription,
|
||||
suffixText: '%',
|
||||
min: 100,
|
||||
max: 300,
|
||||
currentValue: _maxVolume,
|
||||
onSave: (value) async {
|
||||
setState(() => _maxVolume = value);
|
||||
await _settingsService.setMaxVolume(value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showKeyboardShortcutsDialog() {
|
||||
if (_keyboardService == null) return;
|
||||
|
||||
|
||||
@@ -334,8 +334,12 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
}
|
||||
}
|
||||
|
||||
// Apply saved volume
|
||||
final savedVolume = settingsService.getVolume();
|
||||
// Set max volume limit for volume boost
|
||||
final maxVolume = settingsService.getMaxVolume();
|
||||
await player!.setProperty('volume-max', maxVolume.toString());
|
||||
|
||||
// Apply saved volume (clamped to max volume)
|
||||
final savedVolume = settingsService.getVolume().clamp(0.0, maxVolume.toDouble());
|
||||
player!.setVolume(savedVolume);
|
||||
|
||||
// Notify that player is ready
|
||||
@@ -1647,7 +1651,10 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(color: Colors.black.withValues(alpha: 0.5), shape: BoxShape.circle),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const CircularProgressIndicator(color: Colors.white, strokeWidth: 3),
|
||||
),
|
||||
),
|
||||
@@ -1661,9 +1668,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
valueListenable: _isExiting,
|
||||
builder: (context, isExiting, child) {
|
||||
if (!isExiting) return const SizedBox.shrink();
|
||||
return Positioned.fill(
|
||||
child: Container(color: Colors.black),
|
||||
);
|
||||
return Positioned.fill(child: Container(color: Colors.black));
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -14,6 +14,7 @@ class KeyboardShortcutsService {
|
||||
Map<String, HotKey> _hotkeys = {}; // New HotKey objects
|
||||
int _seekTimeSmall = 10; // Default, loaded from settings
|
||||
int _seekTimeLarge = 30; // Default, loaded from settings
|
||||
int _maxVolume = 100; // Default, loaded from settings (100-300%)
|
||||
|
||||
KeyboardShortcutsService._();
|
||||
|
||||
@@ -38,6 +39,7 @@ class KeyboardShortcutsService {
|
||||
_hotkeys = await _settingsService.getKeyboardHotkeys(); // Primary method
|
||||
_seekTimeSmall = _settingsService.getSeekTimeSmall();
|
||||
_seekTimeLarge = _settingsService.getSeekTimeLarge();
|
||||
_maxVolume = _settingsService.getMaxVolume();
|
||||
}
|
||||
|
||||
Map<String, String> get shortcuts => Map.from(_shortcuts);
|
||||
@@ -258,12 +260,12 @@ class KeyboardShortcutsService {
|
||||
player.playOrPause();
|
||||
break;
|
||||
case 'volume_up':
|
||||
final newVolume = (player.state.volume + 10).clamp(0.0, 100.0);
|
||||
final newVolume = (player.state.volume + 10).clamp(0.0, _maxVolume.toDouble());
|
||||
player.setVolume(newVolume);
|
||||
_settingsService.setVolume(newVolume);
|
||||
break;
|
||||
case 'volume_down':
|
||||
final newVolume = (player.state.volume - 10).clamp(0.0, 100.0);
|
||||
final newVolume = (player.state.volume - 10).clamp(0.0, _maxVolume.toDouble());
|
||||
player.setVolume(newVolume);
|
||||
_settingsService.setVolume(newVolume);
|
||||
break;
|
||||
|
||||
@@ -53,6 +53,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
static const String _keyShowPerformanceOverlay = 'show_performance_overlay';
|
||||
static const String _keyMpvConfigEntries = 'mpv_config_entries';
|
||||
static const String _keyMpvConfigPresets = 'mpv_config_presets';
|
||||
static const String _keyMaxVolume = 'max_volume';
|
||||
|
||||
SettingsService._();
|
||||
|
||||
@@ -222,6 +223,15 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
return prefs.getDouble(_keyVolume) ?? 100.0; // Default: full volume
|
||||
}
|
||||
|
||||
// Max Volume (100-300%, for volume boost)
|
||||
Future<void> setMaxVolume(int percent) async {
|
||||
await prefs.setInt(_keyMaxVolume, percent.clamp(100, 300));
|
||||
}
|
||||
|
||||
int getMaxVolume() {
|
||||
return prefs.getInt(_keyMaxVolume) ?? 100; // Default: 100% (no boost)
|
||||
}
|
||||
|
||||
// Rotation Lock (mobile only)
|
||||
Future<void> setRotationLocked(bool locked) async {
|
||||
await prefs.setBool(_keyRotationLocked, locked);
|
||||
@@ -907,6 +917,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
prefs.remove(_keyAudioSyncOffset),
|
||||
prefs.remove(_keySubtitleSyncOffset),
|
||||
prefs.remove(_keyVolume),
|
||||
prefs.remove(_keyMaxVolume),
|
||||
prefs.remove(_keySubtitleFontSize),
|
||||
prefs.remove(_keySubtitleTextColor),
|
||||
prefs.remove(_keySubtitleBorderSize),
|
||||
|
||||
@@ -52,6 +52,24 @@ class _VolumeControlState extends State<VolumeControl> {
|
||||
/// Volume step size for keyboard adjustment.
|
||||
static const double _volumeStep = 5.0;
|
||||
|
||||
/// Maximum volume from settings (100-300).
|
||||
int _maxVolume = 100;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadMaxVolume();
|
||||
}
|
||||
|
||||
Future<void> _loadMaxVolume() async {
|
||||
final settings = await SettingsService.getInstance();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_maxVolume = settings.getMaxVolume();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _enterAdjustMode() {
|
||||
setState(() {
|
||||
_isAdjustMode = true;
|
||||
@@ -66,7 +84,7 @@ class _VolumeControlState extends State<VolumeControl> {
|
||||
|
||||
Future<void> _adjustVolume(double delta) async {
|
||||
final currentVolume = widget.player.state.volume;
|
||||
final newVolume = (currentVolume + delta).clamp(0.0, 100.0);
|
||||
final newVolume = (currentVolume + delta).clamp(0.0, _maxVolume.toDouble());
|
||||
widget.player.setVolume(newVolume);
|
||||
final settings = await SettingsService.getInstance();
|
||||
await settings.setVolume(newVolume);
|
||||
@@ -178,33 +196,58 @@ class _VolumeControlState extends State<VolumeControl> {
|
||||
Widget _buildVolumeSlider(double volume, bool isKeyboardMode) {
|
||||
// Show visual indicator when in adjust mode with keyboard
|
||||
final showAdjustIndicator = _isAdjustMode && isKeyboardMode;
|
||||
final maxVolumeDouble = _maxVolume.toDouble();
|
||||
|
||||
// Calculate 100% marker position as fraction of slider width
|
||||
// Only show marker if max volume > 100
|
||||
final showMarker = _maxVolume > 100;
|
||||
final markerPosition = showMarker ? (100.0 / maxVolumeDouble) : 0.0;
|
||||
|
||||
return SizedBox(
|
||||
width: 100,
|
||||
child: SliderTheme(
|
||||
data: SliderThemeData(
|
||||
trackHeight: showAdjustIndicator ? 4 : 3,
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: showAdjustIndicator ? 8 : 6),
|
||||
overlayShape: const RoundSliderOverlayShape(overlayRadius: 12),
|
||||
),
|
||||
child: Semantics(
|
||||
label: t.videoControls.volumeSlider,
|
||||
slider: true,
|
||||
child: Slider(
|
||||
value: volume,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
onChanged: (value) {
|
||||
widget.player.setVolume(value);
|
||||
},
|
||||
onChangeEnd: (value) async {
|
||||
final settings = await SettingsService.getInstance();
|
||||
await settings.setVolume(value);
|
||||
},
|
||||
activeColor: Colors.white,
|
||||
inactiveColor: Colors.white.withValues(alpha: 0.3),
|
||||
child: Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
// 100% marker (only shown if max > 100)
|
||||
if (showMarker)
|
||||
Positioned(
|
||||
left: 100 * markerPosition - 1, // Adjust for marker width
|
||||
child: Container(
|
||||
width: 2,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.6),
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Volume slider
|
||||
SliderTheme(
|
||||
data: SliderThemeData(
|
||||
trackHeight: showAdjustIndicator ? 4 : 3,
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: showAdjustIndicator ? 8 : 6),
|
||||
overlayShape: const RoundSliderOverlayShape(overlayRadius: 12),
|
||||
),
|
||||
child: Semantics(
|
||||
label: t.videoControls.volumeSlider,
|
||||
slider: true,
|
||||
child: Slider(
|
||||
value: volume.clamp(0.0, maxVolumeDouble),
|
||||
min: 0.0,
|
||||
max: maxVolumeDouble,
|
||||
onChanged: (value) {
|
||||
widget.player.setVolume(value);
|
||||
},
|
||||
onChangeEnd: (value) async {
|
||||
final settings = await SettingsService.getInstance();
|
||||
await settings.setVolume(value);
|
||||
},
|
||||
activeColor: Colors.white,
|
||||
inactiveColor: Colors.white.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user