@tduncan / Cortana / commits / b544b14

fix(tts): prevent just_audio_background RangeError by avoiding empty audio source #86

abdulmominsakib committed Sep 21, 2026 at 08:43 UTC b544b1431b0f4ffa475167c5c57eec224c337379
16 files changed +721 -6
lib/features/tts/providers/tts_providers.dart
+7 -6
@@ -196,8 +196,6 @@ class TtsNotifier extends Notifier<TtsState> {
196 }
197 });
198
199 - _player.setAudioSources([]);
200 -
199 _playerCompleteSubscription = _player.playerStateStream.listen((
200 playerState,
201 ) {
@@ -831,7 +829,7 @@ class TtsNotifier extends Notifier<TtsState> {
829 _isPreview = isPreview;
830 _playlistBuffer.clear();
831 _nextPlaylistIndexToAdd = 0;
834 - await _player.clearAudioSources();
832 + await _player.stop();
833
834 try {
835 AudioSession.instance.then((session) {
@@ -899,7 +897,11 @@ class TtsNotifier extends Notifier<TtsState> {
897 while (_playlistBuffer.containsKey(_nextPlaylistIndexToAdd)) {
898 final indexBeingAdded = _nextPlaylistIndexToAdd;
899 final src = _playlistBuffer.remove(indexBeingAdded)!;
902 - await _player.addAudioSource(src);
900 + if (indexBeingAdded == 0) {
901 + await _player.setAudioSources([src]);
902 + } else {
903 + await _player.addAudioSource(src);
904 + }
905
906 if (indexBeingAdded == 0 && !state.isPaused) {
907 await _player.setSpeed(state.playbackSpeed);
@@ -1092,7 +1094,6 @@ class TtsNotifier extends Notifier<TtsState> {
1094 }
1095 try {
1096 await _player.stop();
1095 - await _player.clearAudioSources();
1097 } catch (_) {}
1098 await _flutterTts?.stop();
1099 _cleanupSessionFiles(_currentSessionId, _chunks.length);
@@ -1172,7 +1173,7 @@ class TtsNotifier extends Notifier<TtsState> {
1173 _isPreview = isPreview;
1174 _playlistBuffer.clear();
1175 _nextPlaylistIndexToAdd = 0;
1175 - await _player.clearAudioSources();
1176 + await _player.stop();
1177
1178 state = state.copyWith(
1179 isSpeaking: true,
lib/l10n/app_localizations_ar.dart
+40
@@ -2167,6 +2167,46 @@ class AppLocalizationsAr extends AppLocalizations {
2167 @override
2168 String get gguf_curated_models_short => 'Curated on-device models';
2169
2170 + @override
2171 + String get gguf_vision_projector => 'Vision Projector';
2172 +
2173 + @override
2174 + String get gguf_attach_projector => 'Attach Vision Projector';
2175 +
2176 + @override
2177 + String get gguf_change_projector => 'Change Vision Projector';
2178 +
2179 + @override
2180 + String get gguf_remove_projector => 'Remove Projector';
2181 +
2182 + @override
2183 + String get gguf_projector_attached =>
2184 + 'Vision projector attached successfully';
2185 +
2186 + @override
2187 + String get gguf_projector_removed => 'Vision projector removed';
2188 +
2189 + @override
2190 + String gguf_projector_auto_detected(String name) {
2191 + return 'Auto-detected and linked vision projector: $name';
2192 + }
2193 +
2194 + @override
2195 + String get gguf_is_projector_file =>
2196 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2197 +
2198 + @override
2199 + String get gguf_projector_url_label =>
2200 + 'Vision Projector URL (optional mmproj)';
2201 +
2202 + @override
2203 + String get gguf_projector_url_hint =>
2204 + 'https://huggingface.co/.../mmproj-...gguf';
2205 +
2206 + @override
2207 + String get gguf_vision_not_supported_error =>
2208 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2209 +
2210 @override
2211 String get execute_tool_title => 'Execute Tool';
2212
lib/l10n/app_localizations_bn.dart
+40
@@ -2188,6 +2188,46 @@ class AppLocalizationsBn extends AppLocalizations {
2188 @override
2189 String get gguf_curated_models_short => 'Curated on-device models';
2190
2191 + @override
2192 + String get gguf_vision_projector => 'Vision Projector';
2193 +
2194 + @override
2195 + String get gguf_attach_projector => 'Attach Vision Projector';
2196 +
2197 + @override
2198 + String get gguf_change_projector => 'Change Vision Projector';
2199 +
2200 + @override
2201 + String get gguf_remove_projector => 'Remove Projector';
2202 +
2203 + @override
2204 + String get gguf_projector_attached =>
2205 + 'Vision projector attached successfully';
2206 +
2207 + @override
2208 + String get gguf_projector_removed => 'Vision projector removed';
2209 +
2210 + @override
2211 + String gguf_projector_auto_detected(String name) {
2212 + return 'Auto-detected and linked vision projector: $name';
2213 + }
2214 +
2215 + @override
2216 + String get gguf_is_projector_file =>
2217 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2218 +
2219 + @override
2220 + String get gguf_projector_url_label =>
2221 + 'Vision Projector URL (optional mmproj)';
2222 +
2223 + @override
2224 + String get gguf_projector_url_hint =>
2225 + 'https://huggingface.co/.../mmproj-...gguf';
2226 +
2227 + @override
2228 + String get gguf_vision_not_supported_error =>
2229 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2230 +
2231 @override
2232 String get execute_tool_title => 'Execute Tool';
2233
lib/l10n/app_localizations_de.dart
+40
@@ -2174,6 +2174,46 @@ class AppLocalizationsDe extends AppLocalizations {
2174 @override
2175 String get gguf_curated_models_short => 'Curated on-device models';
2176
2177 + @override
2178 + String get gguf_vision_projector => 'Vision Projector';
2179 +
2180 + @override
2181 + String get gguf_attach_projector => 'Attach Vision Projector';
2182 +
2183 + @override
2184 + String get gguf_change_projector => 'Change Vision Projector';
2185 +
2186 + @override
2187 + String get gguf_remove_projector => 'Remove Projector';
2188 +
2189 + @override
2190 + String get gguf_projector_attached =>
2191 + 'Vision projector attached successfully';
2192 +
2193 + @override
2194 + String get gguf_projector_removed => 'Vision projector removed';
2195 +
2196 + @override
2197 + String gguf_projector_auto_detected(String name) {
2198 + return 'Auto-detected and linked vision projector: $name';
2199 + }
2200 +
2201 + @override
2202 + String get gguf_is_projector_file =>
2203 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2204 +
2205 + @override
2206 + String get gguf_projector_url_label =>
2207 + 'Vision Projector URL (optional mmproj)';
2208 +
2209 + @override
2210 + String get gguf_projector_url_hint =>
2211 + 'https://huggingface.co/.../mmproj-...gguf';
2212 +
2213 + @override
2214 + String get gguf_vision_not_supported_error =>
2215 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2216 +
2217 @override
2218 String get execute_tool_title => 'Execute Tool';
2219
lib/l10n/app_localizations_es.dart
+40
@@ -2194,6 +2194,46 @@ class AppLocalizationsEs extends AppLocalizations {
2194 @override
2195 String get gguf_curated_models_short => 'Curated on-device models';
2196
2197 + @override
2198 + String get gguf_vision_projector => 'Vision Projector';
2199 +
2200 + @override
2201 + String get gguf_attach_projector => 'Attach Vision Projector';
2202 +
2203 + @override
2204 + String get gguf_change_projector => 'Change Vision Projector';
2205 +
2206 + @override
2207 + String get gguf_remove_projector => 'Remove Projector';
2208 +
2209 + @override
2210 + String get gguf_projector_attached =>
2211 + 'Vision projector attached successfully';
2212 +
2213 + @override
2214 + String get gguf_projector_removed => 'Vision projector removed';
2215 +
2216 + @override
2217 + String gguf_projector_auto_detected(String name) {
2218 + return 'Auto-detected and linked vision projector: $name';
2219 + }
2220 +
2221 + @override
2222 + String get gguf_is_projector_file =>
2223 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2224 +
2225 + @override
2226 + String get gguf_projector_url_label =>
2227 + 'Vision Projector URL (optional mmproj)';
2228 +
2229 + @override
2230 + String get gguf_projector_url_hint =>
2231 + 'https://huggingface.co/.../mmproj-...gguf';
2232 +
2233 + @override
2234 + String get gguf_vision_not_supported_error =>
2235 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2236 +
2237 @override
2238 String get execute_tool_title => 'Execute Tool';
2239
lib/l10n/app_localizations_fr.dart
+40
@@ -2177,6 +2177,46 @@ class AppLocalizationsFr extends AppLocalizations {
2177 @override
2178 String get gguf_curated_models_short => 'Curated on-device models';
2179
2180 + @override
2181 + String get gguf_vision_projector => 'Vision Projector';
2182 +
2183 + @override
2184 + String get gguf_attach_projector => 'Attach Vision Projector';
2185 +
2186 + @override
2187 + String get gguf_change_projector => 'Change Vision Projector';
2188 +
2189 + @override
2190 + String get gguf_remove_projector => 'Remove Projector';
2191 +
2192 + @override
2193 + String get gguf_projector_attached =>
2194 + 'Vision projector attached successfully';
2195 +
2196 + @override
2197 + String get gguf_projector_removed => 'Vision projector removed';
2198 +
2199 + @override
2200 + String gguf_projector_auto_detected(String name) {
2201 + return 'Auto-detected and linked vision projector: $name';
2202 + }
2203 +
2204 + @override
2205 + String get gguf_is_projector_file =>
2206 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2207 +
2208 + @override
2209 + String get gguf_projector_url_label =>
2210 + 'Vision Projector URL (optional mmproj)';
2211 +
2212 + @override
2213 + String get gguf_projector_url_hint =>
2214 + 'https://huggingface.co/.../mmproj-...gguf';
2215 +
2216 + @override
2217 + String get gguf_vision_not_supported_error =>
2218 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2219 +
2220 @override
2221 String get execute_tool_title => 'Execute Tool';
2222
lib/l10n/app_localizations_hi.dart
+40
@@ -2181,6 +2181,46 @@ class AppLocalizationsHi extends AppLocalizations {
2181 @override
2182 String get gguf_curated_models_short => 'Curated on-device models';
2183
2184 + @override
2185 + String get gguf_vision_projector => 'Vision Projector';
2186 +
2187 + @override
2188 + String get gguf_attach_projector => 'Attach Vision Projector';
2189 +
2190 + @override
2191 + String get gguf_change_projector => 'Change Vision Projector';
2192 +
2193 + @override
2194 + String get gguf_remove_projector => 'Remove Projector';
2195 +
2196 + @override
2197 + String get gguf_projector_attached =>
2198 + 'Vision projector attached successfully';
2199 +
2200 + @override
2201 + String get gguf_projector_removed => 'Vision projector removed';
2202 +
2203 + @override
2204 + String gguf_projector_auto_detected(String name) {
2205 + return 'Auto-detected and linked vision projector: $name';
2206 + }
2207 +
2208 + @override
2209 + String get gguf_is_projector_file =>
2210 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2211 +
2212 + @override
2213 + String get gguf_projector_url_label =>
2214 + 'Vision Projector URL (optional mmproj)';
2215 +
2216 + @override
2217 + String get gguf_projector_url_hint =>
2218 + 'https://huggingface.co/.../mmproj-...gguf';
2219 +
2220 + @override
2221 + String get gguf_vision_not_supported_error =>
2222 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2223 +
2224 @override
2225 String get execute_tool_title => 'Execute Tool';
2226
lib/l10n/app_localizations_it.dart
+40
@@ -2195,6 +2195,46 @@ class AppLocalizationsIt extends AppLocalizations {
2195 @override
2196 String get gguf_curated_models_short => 'Curated on-device models';
2197
2198 + @override
2199 + String get gguf_vision_projector => 'Vision Projector';
2200 +
2201 + @override
2202 + String get gguf_attach_projector => 'Attach Vision Projector';
2203 +
2204 + @override
2205 + String get gguf_change_projector => 'Change Vision Projector';
2206 +
2207 + @override
2208 + String get gguf_remove_projector => 'Remove Projector';
2209 +
2210 + @override
2211 + String get gguf_projector_attached =>
2212 + 'Vision projector attached successfully';
2213 +
2214 + @override
2215 + String get gguf_projector_removed => 'Vision projector removed';
2216 +
2217 + @override
2218 + String gguf_projector_auto_detected(String name) {
2219 + return 'Auto-detected and linked vision projector: $name';
2220 + }
2221 +
2222 + @override
2223 + String get gguf_is_projector_file =>
2224 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2225 +
2226 + @override
2227 + String get gguf_projector_url_label =>
2228 + 'Vision Projector URL (optional mmproj)';
2229 +
2230 + @override
2231 + String get gguf_projector_url_hint =>
2232 + 'https://huggingface.co/.../mmproj-...gguf';
2233 +
2234 + @override
2235 + String get gguf_vision_not_supported_error =>
2236 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2237 +
2238 @override
2239 String get execute_tool_title => 'Execute Tool';
2240
lib/l10n/app_localizations_ja.dart
+40
@@ -2136,6 +2136,46 @@ class AppLocalizationsJa extends AppLocalizations {
2136 @override
2137 String get gguf_curated_models_short => 'Curated on-device models';
2138
2139 + @override
2140 + String get gguf_vision_projector => 'Vision Projector';
2141 +
2142 + @override
2143 + String get gguf_attach_projector => 'Attach Vision Projector';
2144 +
2145 + @override
2146 + String get gguf_change_projector => 'Change Vision Projector';
2147 +
2148 + @override
2149 + String get gguf_remove_projector => 'Remove Projector';
2150 +
2151 + @override
2152 + String get gguf_projector_attached =>
2153 + 'Vision projector attached successfully';
2154 +
2155 + @override
2156 + String get gguf_projector_removed => 'Vision projector removed';
2157 +
2158 + @override
2159 + String gguf_projector_auto_detected(String name) {
2160 + return 'Auto-detected and linked vision projector: $name';
2161 + }
2162 +
2163 + @override
2164 + String get gguf_is_projector_file =>
2165 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2166 +
2167 + @override
2168 + String get gguf_projector_url_label =>
2169 + 'Vision Projector URL (optional mmproj)';
2170 +
2171 + @override
2172 + String get gguf_projector_url_hint =>
2173 + 'https://huggingface.co/.../mmproj-...gguf';
2174 +
2175 + @override
2176 + String get gguf_vision_not_supported_error =>
2177 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2178 +
2179 @override
2180 String get execute_tool_title => 'Execute Tool';
2181
lib/l10n/app_localizations_ko.dart
+40
@@ -2127,6 +2127,46 @@ class AppLocalizationsKo extends AppLocalizations {
2127 @override
2128 String get gguf_curated_models_short => '선별된 온디바이스 모델';
2129
2130 + @override
2131 + String get gguf_vision_projector => 'Vision Projector';
2132 +
2133 + @override
2134 + String get gguf_attach_projector => 'Attach Vision Projector';
2135 +
2136 + @override
2137 + String get gguf_change_projector => 'Change Vision Projector';
2138 +
2139 + @override
2140 + String get gguf_remove_projector => 'Remove Projector';
2141 +
2142 + @override
2143 + String get gguf_projector_attached =>
2144 + 'Vision projector attached successfully';
2145 +
2146 + @override
2147 + String get gguf_projector_removed => 'Vision projector removed';
2148 +
2149 + @override
2150 + String gguf_projector_auto_detected(String name) {
2151 + return 'Auto-detected and linked vision projector: $name';
2152 + }
2153 +
2154 + @override
2155 + String get gguf_is_projector_file =>
2156 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2157 +
2158 + @override
2159 + String get gguf_projector_url_label =>
2160 + 'Vision Projector URL (optional mmproj)';
2161 +
2162 + @override
2163 + String get gguf_projector_url_hint =>
2164 + 'https://huggingface.co/.../mmproj-...gguf';
2165 +
2166 + @override
2167 + String get gguf_vision_not_supported_error =>
2168 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2169 +
2170 @override
2171 String get execute_tool_title => '도구 실행';
2172
lib/l10n/app_localizations_pt.dart
+40
@@ -2194,6 +2194,46 @@ class AppLocalizationsPt extends AppLocalizations {
2194 @override
2195 String get gguf_curated_models_short => 'Modelos selecionados no dispositivo';
2196
2197 + @override
2198 + String get gguf_vision_projector => 'Vision Projector';
2199 +
2200 + @override
2201 + String get gguf_attach_projector => 'Attach Vision Projector';
2202 +
2203 + @override
2204 + String get gguf_change_projector => 'Change Vision Projector';
2205 +
2206 + @override
2207 + String get gguf_remove_projector => 'Remove Projector';
2208 +
2209 + @override
2210 + String get gguf_projector_attached =>
2211 + 'Vision projector attached successfully';
2212 +
2213 + @override
2214 + String get gguf_projector_removed => 'Vision projector removed';
2215 +
2216 + @override
2217 + String gguf_projector_auto_detected(String name) {
2218 + return 'Auto-detected and linked vision projector: $name';
2219 + }
2220 +
2221 + @override
2222 + String get gguf_is_projector_file =>
2223 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2224 +
2225 + @override
2226 + String get gguf_projector_url_label =>
2227 + 'Vision Projector URL (optional mmproj)';
2228 +
2229 + @override
2230 + String get gguf_projector_url_hint =>
2231 + 'https://huggingface.co/.../mmproj-...gguf';
2232 +
2233 + @override
2234 + String get gguf_vision_not_supported_error =>
2235 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2236 +
2237 @override
2238 String get execute_tool_title => 'Executar ferramenta';
2239
lib/l10n/app_localizations_ru.dart
+40
@@ -2192,6 +2192,46 @@ class AppLocalizationsRu extends AppLocalizations {
2192 @override
2193 String get gguf_curated_models_short => 'Кураторские модели на устройстве';
2194
2195 + @override
2196 + String get gguf_vision_projector => 'Vision Projector';
2197 +
2198 + @override
2199 + String get gguf_attach_projector => 'Attach Vision Projector';
2200 +
2201 + @override
2202 + String get gguf_change_projector => 'Change Vision Projector';
2203 +
2204 + @override
2205 + String get gguf_remove_projector => 'Remove Projector';
2206 +
2207 + @override
2208 + String get gguf_projector_attached =>
2209 + 'Vision projector attached successfully';
2210 +
2211 + @override
2212 + String get gguf_projector_removed => 'Vision projector removed';
2213 +
2214 + @override
2215 + String gguf_projector_auto_detected(String name) {
2216 + return 'Auto-detected and linked vision projector: $name';
2217 + }
2218 +
2219 + @override
2220 + String get gguf_is_projector_file =>
2221 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2222 +
2223 + @override
2224 + String get gguf_projector_url_label =>
2225 + 'Vision Projector URL (optional mmproj)';
2226 +
2227 + @override
2228 + String get gguf_projector_url_hint =>
2229 + 'https://huggingface.co/.../mmproj-...gguf';
2230 +
2231 + @override
2232 + String get gguf_vision_not_supported_error =>
2233 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2234 +
2235 @override
2236 String get execute_tool_title => 'Выполнить инструмент';
2237
lib/l10n/app_localizations_tr.dart
+40
@@ -2190,6 +2190,46 @@ class AppLocalizationsTr extends AppLocalizations {
2190 @override
2191 String get gguf_curated_models_short => 'Curated on-device models';
2192
2193 + @override
2194 + String get gguf_vision_projector => 'Vision Projector';
2195 +
2196 + @override
2197 + String get gguf_attach_projector => 'Attach Vision Projector';
2198 +
2199 + @override
2200 + String get gguf_change_projector => 'Change Vision Projector';
2201 +
2202 + @override
2203 + String get gguf_remove_projector => 'Remove Projector';
2204 +
2205 + @override
2206 + String get gguf_projector_attached =>
2207 + 'Vision projector attached successfully';
2208 +
2209 + @override
2210 + String get gguf_projector_removed => 'Vision projector removed';
2211 +
2212 + @override
2213 + String gguf_projector_auto_detected(String name) {
2214 + return 'Auto-detected and linked vision projector: $name';
2215 + }
2216 +
2217 + @override
2218 + String get gguf_is_projector_file =>
2219 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2220 +
2221 + @override
2222 + String get gguf_projector_url_label =>
2223 + 'Vision Projector URL (optional mmproj)';
2224 +
2225 + @override
2226 + String get gguf_projector_url_hint =>
2227 + 'https://huggingface.co/.../mmproj-...gguf';
2228 +
2229 + @override
2230 + String get gguf_vision_not_supported_error =>
2231 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2232 +
2233 @override
2234 String get execute_tool_title => 'Execute Tool';
2235
lib/l10n/app_localizations_zh.dart
+40
@@ -2121,6 +2121,46 @@ class AppLocalizationsZh extends AppLocalizations {
2121 @override
2122 String get gguf_curated_models_short => 'Curated on-device models';
2123
2124 + @override
2125 + String get gguf_vision_projector => 'Vision Projector';
2126 +
2127 + @override
2128 + String get gguf_attach_projector => 'Attach Vision Projector';
2129 +
2130 + @override
2131 + String get gguf_change_projector => 'Change Vision Projector';
2132 +
2133 + @override
2134 + String get gguf_remove_projector => 'Remove Projector';
2135 +
2136 + @override
2137 + String get gguf_projector_attached =>
2138 + 'Vision projector attached successfully';
2139 +
2140 + @override
2141 + String get gguf_projector_removed => 'Vision projector removed';
2142 +
2143 + @override
2144 + String gguf_projector_auto_detected(String name) {
2145 + return 'Auto-detected and linked vision projector: $name';
2146 + }
2147 +
2148 + @override
2149 + String get gguf_is_projector_file =>
2150 + 'The selected file is a vision projector (mmproj), not a standalone model.';
2151 +
2152 + @override
2153 + String get gguf_projector_url_label =>
2154 + 'Vision Projector URL (optional mmproj)';
2155 +
2156 + @override
2157 + String get gguf_projector_url_hint =>
2158 + 'https://huggingface.co/.../mmproj-...gguf';
2159 +
2160 + @override
2161 + String get gguf_vision_not_supported_error =>
2162 + 'The active model does not support image attachments. Please attach a vision projector (mmproj) or select a vision-supported model.';
2163 +
2164 @override
2165 String get execute_tool_title => 'Execute Tool';
2166
lib/l10n/untranslated.txt
+154
@@ -1,5 +1,16 @@
1 {
2 "ar": [
3 + "gguf_vision_projector",
4 + "gguf_attach_projector",
5 + "gguf_change_projector",
6 + "gguf_remove_projector",
7 + "gguf_projector_attached",
8 + "gguf_projector_removed",
9 + "gguf_projector_auto_detected",
10 + "gguf_is_projector_file",
11 + "gguf_projector_url_label",
12 + "gguf_projector_url_hint",
13 + "gguf_vision_not_supported_error",
14 "calendar_access",
15 "calendar_access_desc",
16 "calendar_permission_denied",
@@ -16,6 +27,17 @@
27 ],
28
29 "bn": [
30 + "gguf_vision_projector",
31 + "gguf_attach_projector",
32 + "gguf_change_projector",
33 + "gguf_remove_projector",
34 + "gguf_projector_attached",
35 + "gguf_projector_removed",
36 + "gguf_projector_auto_detected",
37 + "gguf_is_projector_file",
38 + "gguf_projector_url_label",
39 + "gguf_projector_url_hint",
40 + "gguf_vision_not_supported_error",
41 "file_explorer_not_found",
42 "export_data_failed",
43 "file_pick_failed",
@@ -36,6 +58,17 @@
58 ],
59
60 "de": [
61 + "gguf_vision_projector",
62 + "gguf_attach_projector",
63 + "gguf_change_projector",
64 + "gguf_remove_projector",
65 + "gguf_projector_attached",
66 + "gguf_projector_removed",
67 + "gguf_projector_auto_detected",
68 + "gguf_is_projector_file",
69 + "gguf_projector_url_label",
70 + "gguf_projector_url_hint",
71 + "gguf_vision_not_supported_error",
72 "file_explorer_not_found",
73 "export_data_failed",
74 "file_pick_failed",
@@ -56,6 +89,17 @@
89 ],
90
91 "es": [
92 + "gguf_vision_projector",
93 + "gguf_attach_projector",
94 + "gguf_change_projector",
95 + "gguf_remove_projector",
96 + "gguf_projector_attached",
97 + "gguf_projector_removed",
98 + "gguf_projector_auto_detected",
99 + "gguf_is_projector_file",
100 + "gguf_projector_url_label",
101 + "gguf_projector_url_hint",
102 + "gguf_vision_not_supported_error",
103 "file_explorer_not_found",
104 "export_data_failed",
105 "file_pick_failed",
@@ -76,6 +120,17 @@
120 ],
121
122 "fr": [
123 + "gguf_vision_projector",
124 + "gguf_attach_projector",
125 + "gguf_change_projector",
126 + "gguf_remove_projector",
127 + "gguf_projector_attached",
128 + "gguf_projector_removed",
129 + "gguf_projector_auto_detected",
130 + "gguf_is_projector_file",
131 + "gguf_projector_url_label",
132 + "gguf_projector_url_hint",
133 + "gguf_vision_not_supported_error",
134 "file_explorer_not_found",
135 "export_data_failed",
136 "file_pick_failed",
@@ -96,6 +151,17 @@
151 ],
152
153 "hi": [
154 + "gguf_vision_projector",
155 + "gguf_attach_projector",
156 + "gguf_change_projector",
157 + "gguf_remove_projector",
158 + "gguf_projector_attached",
159 + "gguf_projector_removed",
160 + "gguf_projector_auto_detected",
161 + "gguf_is_projector_file",
162 + "gguf_projector_url_label",
163 + "gguf_projector_url_hint",
164 + "gguf_vision_not_supported_error",
165 "file_explorer_not_found",
166 "export_data_failed",
167 "file_pick_failed",
@@ -116,6 +182,17 @@
182 ],
183
184 "it": [
185 + "gguf_vision_projector",
186 + "gguf_attach_projector",
187 + "gguf_change_projector",
188 + "gguf_remove_projector",
189 + "gguf_projector_attached",
190 + "gguf_projector_removed",
191 + "gguf_projector_auto_detected",
192 + "gguf_is_projector_file",
193 + "gguf_projector_url_label",
194 + "gguf_projector_url_hint",
195 + "gguf_vision_not_supported_error",
196 "file_explorer_not_found",
197 "export_data_failed",
198 "file_pick_failed",
@@ -136,6 +213,17 @@
213 ],
214
215 "ja": [
216 + "gguf_vision_projector",
217 + "gguf_attach_projector",
218 + "gguf_change_projector",
219 + "gguf_remove_projector",
220 + "gguf_projector_attached",
221 + "gguf_projector_removed",
222 + "gguf_projector_auto_detected",
223 + "gguf_is_projector_file",
224 + "gguf_projector_url_label",
225 + "gguf_projector_url_hint",
226 + "gguf_vision_not_supported_error",
227 "file_explorer_not_found",
228 "export_data_failed",
229 "file_pick_failed",
@@ -156,18 +244,51 @@
244 ],
245
246 "ko": [
247 + "gguf_vision_projector",
248 + "gguf_attach_projector",
249 + "gguf_change_projector",
250 + "gguf_remove_projector",
251 + "gguf_projector_attached",
252 + "gguf_projector_removed",
253 + "gguf_projector_auto_detected",
254 + "gguf_is_projector_file",
255 + "gguf_projector_url_label",
256 + "gguf_projector_url_hint",
257 + "gguf_vision_not_supported_error",
258 "sms_access",
259 "sms_access_desc",
260 "sms_permission_denied"
261 ],
262
263 "pt": [
264 + "gguf_vision_projector",
265 + "gguf_attach_projector",
266 + "gguf_change_projector",
267 + "gguf_remove_projector",
268 + "gguf_projector_attached",
269 + "gguf_projector_removed",
270 + "gguf_projector_auto_detected",
271 + "gguf_is_projector_file",
272 + "gguf_projector_url_label",
273 + "gguf_projector_url_hint",
274 + "gguf_vision_not_supported_error",
275 "sms_access",
276 "sms_access_desc",
277 "sms_permission_denied"
278 ],
279
280 "ru": [
281 + "gguf_vision_projector",
282 + "gguf_attach_projector",
283 + "gguf_change_projector",
284 + "gguf_remove_projector",
285 + "gguf_projector_attached",
286 + "gguf_projector_removed",
287 + "gguf_projector_auto_detected",
288 + "gguf_is_projector_file",
289 + "gguf_projector_url_label",
290 + "gguf_projector_url_hint",
291 + "gguf_vision_not_supported_error",
292 "file_explorer_not_found",
293 "export_data_failed",
294 "file_pick_failed",
@@ -188,6 +309,17 @@
309 ],
310
311 "tr": [
312 + "gguf_vision_projector",
313 + "gguf_attach_projector",
314 + "gguf_change_projector",
315 + "gguf_remove_projector",
316 + "gguf_projector_attached",
317 + "gguf_projector_removed",
318 + "gguf_projector_auto_detected",
319 + "gguf_is_projector_file",
320 + "gguf_projector_url_label",
321 + "gguf_projector_url_hint",
322 + "gguf_vision_not_supported_error",
323 "file_explorer_not_found",
324 "export_data_failed",
325 "file_pick_failed",
@@ -208,6 +340,17 @@
340 ],
341
342 "zh": [
343 + "gguf_vision_projector",
344 + "gguf_attach_projector",
345 + "gguf_change_projector",
346 + "gguf_remove_projector",
347 + "gguf_projector_attached",
348 + "gguf_projector_removed",
349 + "gguf_projector_auto_detected",
350 + "gguf_is_projector_file",
351 + "gguf_projector_url_label",
352 + "gguf_projector_url_hint",
353 + "gguf_vision_not_supported_error",
354 "file_explorer_not_found",
355 "export_data_failed",
356 "file_pick_failed",
@@ -228,6 +371,17 @@
371 ],
372
373 "zh_TW": [
374 + "gguf_vision_projector",
375 + "gguf_attach_projector",
376 + "gguf_change_projector",
377 + "gguf_remove_projector",
378 + "gguf_projector_attached",
379 + "gguf_projector_removed",
380 + "gguf_projector_auto_detected",
381 + "gguf_is_projector_file",
382 + "gguf_projector_url_label",
383 + "gguf_projector_url_hint",
384 + "gguf_vision_not_supported_error",
385 "file_explorer_not_found",
386 "export_data_failed",
387 "file_pick_failed",
test/features/issue_crash_fixes_test.dart
+40
@@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
7 import 'package:flutter_test/flutter_test.dart';
8 import 'package:just_audio/just_audio.dart';
9 import 'package:just_audio_background/just_audio_background.dart';
10 +import 'package:localmind/features/tts/providers/tts_providers.dart';
11 import 'package:localmind/bootstrap/safe_riverpod_scope_host.dart';
12 import 'package:localmind/core/models/enums.dart';
13 import 'package:localmind/core/providers/app_providers.dart';
@@ -1090,6 +1091,45 @@ void main() {
1091 );
1092 },
1093 );
1094 +
1095 + group('Issue #86: TTS Empty AudioSource RangeError Prevention', () {
1096 + test(
1097 + 'ConcatenatingAudioSource is initialized with at least one chunk to avoid RangeError',
1098 + () {
1099 + final tempDir = Directory.systemTemp;
1100 + final tempFile = File('${tempDir.path}/tts_chunk_test_86.wav');
1101 + tempFile.writeAsBytesSync(Uint8List(100));
1102 + addTearDown(() {
1103 + if (tempFile.existsSync()) tempFile.deleteSync();
1104 + });
1105 +
1106 + final firstSource = AudioSource.file(
1107 + tempFile.path,
1108 + tag: const MediaItem(
1109 + id: 'tts_chunk_0',
1110 + album: 'LocalMind TTS',
1111 + title: 'Test chunk',
1112 + ),
1113 + );
1114 +
1115 + // ignore: deprecated_member_use
1116 + final playlist = ConcatenatingAudioSource(children: [firstSource]);
1117 + expect(playlist.children, isNotEmpty);
1118 + expect(playlist.children.length, 1);
1119 + expect(playlist.children.first, equals(firstSource));
1120 + },
1121 + );
1122 +
1123 + test(
1124 + 'TtsNotifier does not initialize player with empty AudioSources on startup',
1125 + () {
1126 + final notifier = TtsNotifier();
1127 + // On startup before playback begins, player should not have an empty audioSource
1128 + // which causes RangeError: Valid value range is empty: 0 in just_audio_background.
1129 + expect(notifier.player.audioSource, isNull);
1130 + },
1131 + );
1132 + });
1133 }
1134
1135 class _MutableActiveServerNotifier extends ActiveServerNotifier {