react-native-sherpa-onnx 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +7 -2
  2. package/SherpaOnnx.podspec +4 -1
  3. package/android/prebuilt-download.gradle +23 -23
  4. package/android/src/main/assets/model_licenses/asr-models-license-status.csv +1 -0
  5. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +23 -0
  6. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +9 -0
  7. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +51 -8
  8. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-tts.cpp +31 -4
  9. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +19 -1
  10. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +5 -0
  11. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-tts-wrapper.cpp +7 -0
  12. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-stt.cpp +11 -0
  13. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-validate-tts.cpp +14 -0
  14. package/android/src/main/java/com/sherpaonnx/SherpaOnnxArchiveHelper.kt +110 -35
  15. package/android/src/main/java/com/sherpaonnx/SherpaOnnxExtractionNotificationHelper.kt +102 -0
  16. package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +92 -18
  17. package/android/src/main/java/com/sherpaonnx/SherpaOnnxSttHelper.kt +22 -0
  18. package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +15 -0
  19. package/ios/Resources/model_licenses/asr-models-license-status.csv +1 -0
  20. package/ios/SherpaOnnx+STT.mm +13 -1
  21. package/ios/SherpaOnnx+TTS.mm +1 -0
  22. package/ios/SherpaOnnx.mm +87 -17
  23. package/ios/model_detect/sherpa-onnx-model-detect-helper.h +5 -0
  24. package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +23 -0
  25. package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +51 -7
  26. package/ios/model_detect/sherpa-onnx-model-detect-tts.mm +36 -4
  27. package/ios/model_detect/sherpa-onnx-model-detect.h +19 -1
  28. package/ios/model_detect/sherpa-onnx-validate-stt.mm +11 -0
  29. package/ios/model_detect/sherpa-onnx-validate-tts.mm +14 -0
  30. package/ios/stt/sherpa-onnx-stt-wrapper.h +11 -1
  31. package/ios/stt/sherpa-onnx-stt-wrapper.mm +30 -2
  32. package/ios/tts/sherpa-onnx-tts-wrapper.mm +25 -0
  33. package/lib/module/NativeSherpaOnnx.js.map +1 -1
  34. package/lib/module/download/ModelDownloadManager.js +1 -1
  35. package/lib/module/download/ModelDownloadManager.js.map +1 -1
  36. package/lib/module/download/background-downloader-types.js +2 -0
  37. package/lib/module/download/background-downloader-types.js.map +1 -0
  38. package/lib/module/download/downloadTask.js +54 -1
  39. package/lib/module/download/downloadTask.js.map +1 -1
  40. package/lib/module/download/index.js +1 -1
  41. package/lib/module/download/index.js.map +1 -1
  42. package/lib/module/download/postDownloadProcessing.js +17 -4
  43. package/lib/module/download/postDownloadProcessing.js.map +1 -1
  44. package/lib/module/download/registry.js +1 -0
  45. package/lib/module/download/registry.js.map +1 -1
  46. package/lib/module/extraction/extractTarBz2.js +2 -2
  47. package/lib/module/extraction/extractTarBz2.js.map +1 -1
  48. package/lib/module/extraction/extractTarZst.js +2 -2
  49. package/lib/module/extraction/extractTarZst.js.map +1 -1
  50. package/lib/module/extraction/index.js +10 -5
  51. package/lib/module/extraction/index.js.map +1 -1
  52. package/lib/module/stt/index.js +4 -2
  53. package/lib/module/stt/index.js.map +1 -1
  54. package/lib/module/stt/streaming.js +2 -1
  55. package/lib/module/stt/streaming.js.map +1 -1
  56. package/lib/module/stt/types.js +3 -1
  57. package/lib/module/stt/types.js.map +1 -1
  58. package/lib/module/tts/index.js +5 -3
  59. package/lib/module/tts/index.js.map +1 -1
  60. package/lib/module/tts/streaming.js +4 -2
  61. package/lib/module/tts/streaming.js.map +1 -1
  62. package/lib/module/tts/types.js +4 -1
  63. package/lib/module/tts/types.js.map +1 -1
  64. package/lib/typescript/src/NativeSherpaOnnx.d.ts +26 -10
  65. package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
  66. package/lib/typescript/src/download/ModelDownloadManager.d.ts +2 -1
  67. package/lib/typescript/src/download/ModelDownloadManager.d.ts.map +1 -1
  68. package/lib/typescript/src/download/background-downloader-types.d.ts +64 -0
  69. package/lib/typescript/src/download/background-downloader-types.d.ts.map +1 -0
  70. package/lib/typescript/src/download/downloadTask.d.ts +10 -0
  71. package/lib/typescript/src/download/downloadTask.d.ts.map +1 -1
  72. package/lib/typescript/src/download/index.d.ts +2 -2
  73. package/lib/typescript/src/download/index.d.ts.map +1 -1
  74. package/lib/typescript/src/download/postDownloadProcessing.d.ts +9 -0
  75. package/lib/typescript/src/download/postDownloadProcessing.d.ts.map +1 -1
  76. package/lib/typescript/src/download/registry.d.ts.map +1 -1
  77. package/lib/typescript/src/extraction/extractTarBz2.d.ts +2 -1
  78. package/lib/typescript/src/extraction/extractTarBz2.d.ts.map +1 -1
  79. package/lib/typescript/src/extraction/extractTarZst.d.ts +2 -1
  80. package/lib/typescript/src/extraction/extractTarZst.d.ts.map +1 -1
  81. package/lib/typescript/src/extraction/index.d.ts +1 -1
  82. package/lib/typescript/src/extraction/index.d.ts.map +1 -1
  83. package/lib/typescript/src/extraction/types.d.ts +12 -0
  84. package/lib/typescript/src/extraction/types.d.ts.map +1 -1
  85. package/lib/typescript/src/stt/index.d.ts +1 -1
  86. package/lib/typescript/src/stt/index.d.ts.map +1 -1
  87. package/lib/typescript/src/stt/streaming.d.ts.map +1 -1
  88. package/lib/typescript/src/stt/types.d.ts +16 -1
  89. package/lib/typescript/src/stt/types.d.ts.map +1 -1
  90. package/lib/typescript/src/tts/index.d.ts +1 -1
  91. package/lib/typescript/src/tts/index.d.ts.map +1 -1
  92. package/lib/typescript/src/tts/streaming.d.ts.map +1 -1
  93. package/lib/typescript/src/tts/types.d.ts +6 -1
  94. package/lib/typescript/src/tts/types.d.ts.map +1 -1
  95. package/package.json +1 -1
  96. package/scripts/ci/update_model_license_csv.sh +16 -16
  97. package/src/NativeSherpaOnnx.ts +38 -11
  98. package/src/download/ModelDownloadManager.ts +2 -0
  99. package/src/download/background-downloader-types.ts +73 -0
  100. package/src/download/downloadTask.ts +68 -0
  101. package/src/download/index.ts +2 -0
  102. package/src/download/postDownloadProcessing.ts +24 -1
  103. package/src/download/registry.ts +1 -0
  104. package/src/extraction/extractTarBz2.ts +7 -2
  105. package/src/extraction/extractTarZst.ts +7 -2
  106. package/src/extraction/index.ts +29 -6
  107. package/src/extraction/types.ts +16 -0
  108. package/src/stt/index.ts +8 -7
  109. package/src/stt/streaming.ts +7 -1
  110. package/src/stt/types.ts +18 -0
  111. package/src/tts/index.ts +10 -7
  112. package/src/tts/streaming.ts +8 -3
  113. package/src/tts/types.ts +9 -0
  114. package/third_party/sherpa-onnx-prebuilt/ANDROID_RELEASE_TAG +1 -1
  115. package/third_party/sherpa-onnx-prebuilt/IOS_RELEASE_TAG +1 -1
  116. package/lib/module/download/background-downloader.d.js +0 -2
  117. package/lib/module/download/background-downloader.d.js.map +0 -1
  118. package/src/download/background-downloader.d.ts +0 -43
@@ -14,7 +14,7 @@
14
14
  # - Uses tree-cache (from asr/tts-models-structure.txt + new downloads) to see if a LICENSE-like
15
15
  # path exists — no full extract unless we need file contents for detection.
16
16
  # - Downloads the .tar.bz2 only when a license-like path was found and license_type is still empty.
17
- # - Pipeline: try archive (if applicable) HF/ModelScope fallbacks for eligible assets. If no license
17
+ # - Pipeline: try archive (if applicable) --> HF/ModelScope fallbacks for eligible assets. If no license
18
18
  # is found after all attempts, license_type is set to exhausted (default keyword; override with
19
19
  # LICENSE_EXHAUSTED env). You can set exhausted manually after review.
20
20
  # - .onnx-only: exhausted (no archive to scan).
@@ -30,7 +30,7 @@
30
30
  # asr-models-license-status.csv (default: same directory as --csv). Strip prefix
31
31
  # sherpa-onnx-qnn-<soc>-binary-<n>-seconds- from the QNN asset name, then try a few derived filenames
32
32
  # (exact, sherpa-onnx-…, and sherpa-onnx-<stem>.tar.bz2 when …-int8.tar.bz2). On match, copy the ASR row’s
33
- # license fields (not asset_name) onto the QNN asset; on no match exhausted like other dead ends.
33
+ # license fields (not asset_name) onto the QNN asset; on no match --> exhausted like other dead ends.
34
34
  # - Hugging Face: set HF_TOKEN or HUGGINGFACE_HUB_TOKEN (read token is enough for public repos). Anonymous
35
35
  # requests from CI often get HTTP 401; without a token README/MODEL_CARD cannot be fetched.
36
36
  #
@@ -566,7 +566,7 @@ for asset_name in "${release_assets[@]}"; do
566
566
 
567
567
  if [[ "$asset_name" == *.onnx ]]; then
568
568
  set_exhausted "$asset_name"
569
- echo " $asset_name — .onnx bundle license_type=$LICENSE_EXHAUSTED (no archive; skipped next run)"
569
+ echo " $asset_name — .onnx bundle --> license_type=$LICENSE_EXHAUSTED (no archive; skipped next run)"
570
570
  continue
571
571
  fi
572
572
 
@@ -601,15 +601,15 @@ for asset_name in "${release_assets[@]}"; do
601
601
 
602
602
  if [[ ${#license_paths[@]} -eq 0 ]]; then
603
603
  if try_hf_model_card_fallback "$asset_name"; then
604
- echo " $asset_name — no license in tree filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
604
+ echo " $asset_name — no license in tree --> filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
605
605
  continue
606
606
  fi
607
607
  if try_qnn_asr_license_fallback "$asset_name"; then
608
- echo " $asset_name — no license in tree + HF exhausted QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
608
+ echo " $asset_name — no license in tree + HF exhausted --> QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
609
609
  continue
610
610
  fi
611
611
  set_exhausted "$asset_name"
612
- echo " $asset_name — no license in tree + fallbacks exhausted license_type=$LICENSE_EXHAUSTED"
612
+ echo " $asset_name — no license in tree + fallbacks exhausted --> license_type=$LICENSE_EXHAUSTED"
613
613
  continue
614
614
  fi
615
615
 
@@ -624,15 +624,15 @@ for asset_name in "${release_assets[@]}"; do
624
624
  if ! curl "${_curl_dl[@]}" -o "$archive_path" "$url"; then
625
625
  rm -rf "$td"
626
626
  if try_hf_model_card_fallback "$asset_name"; then
627
- echo " $asset_name — download failed filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
627
+ echo " $asset_name — download failed --> filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
628
628
  continue
629
629
  fi
630
630
  if try_qnn_asr_license_fallback "$asset_name"; then
631
- echo " $asset_name — download failed + HF exhausted QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
631
+ echo " $asset_name — download failed + HF exhausted --> QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
632
632
  continue
633
633
  fi
634
634
  set_exhausted "$asset_name"
635
- echo " $asset_name — download failed + fallbacks exhausted license_type=$LICENSE_EXHAUSTED"
635
+ echo " $asset_name — download failed + fallbacks exhausted --> license_type=$LICENSE_EXHAUSTED"
636
636
  continue
637
637
  fi
638
638
 
@@ -665,15 +665,15 @@ for asset_name in "${release_assets[@]}"; do
665
665
  if [[ -z "$extracted_text" ]]; then
666
666
  rm -rf "$td"
667
667
  if try_hf_model_card_fallback "$asset_name"; then
668
- echo " $asset_name — could not extract license file filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
668
+ echo " $asset_name — could not extract license file --> filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
669
669
  continue
670
670
  fi
671
671
  if try_qnn_asr_license_fallback "$asset_name"; then
672
- echo " $asset_name — could not extract license + HF exhausted QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
672
+ echo " $asset_name — could not extract license + HF exhausted --> QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
673
673
  continue
674
674
  fi
675
675
  set_exhausted "$asset_name"
676
- echo " $asset_name — could not extract license file + fallbacks exhausted license_type=$LICENSE_EXHAUSTED"
676
+ echo " $asset_name — could not extract license file + fallbacks exhausted --> license_type=$LICENSE_EXHAUSTED"
677
677
  continue
678
678
  fi
679
679
 
@@ -686,15 +686,15 @@ for asset_name in "${release_assets[@]}"; do
686
686
 
687
687
  if [[ "$l_res" == "unknown" ]]; then
688
688
  if try_hf_model_card_fallback "$asset_name"; then
689
- echo " $asset_name — archive license text unknown filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
689
+ echo " $asset_name — archive license text unknown --> filled from $(log_license_fallback_source "$asset_name") (license_type=${existing_license_type["$asset_name"]})"
690
690
  continue
691
691
  fi
692
692
  if try_qnn_asr_license_fallback "$asset_name"; then
693
- echo " $asset_name — archive text unknown + HF exhausted QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
693
+ echo " $asset_name — archive text unknown + HF exhausted --> QNN mirror from asr row (${_QNN_ASR_MIRROR_MATCHED}) (license_type=${existing_license_type["$asset_name"]})"
694
694
  continue
695
695
  fi
696
696
  set_exhausted "$asset_name"
697
- echo " $asset_name — archive text unclassified + fallbacks exhausted license_type=$LICENSE_EXHAUSTED"
697
+ echo " $asset_name — archive text unclassified + fallbacks exhausted --> license_type=$LICENSE_EXHAUSTED"
698
698
  continue
699
699
  fi
700
700
 
@@ -761,5 +761,5 @@ if [[ -d "$REPO_ROOT/android" && -d "$REPO_ROOT/ios" && -f "$CSV_FILE" ]]; then
761
761
  if ! same_canonical_path "$CSV_FILE" "$_ios_target"; then
762
762
  cp "$CSV_FILE" "$_ios_target"
763
763
  fi
764
- echo "Synced $_bn android/src/main/assets/model_licenses/ and ios/Resources/model_licenses/"
764
+ echo "Synced $_bn --> android/src/main/assets/model_licenses/ and ios/Resources/model_licenses/"
765
765
  fi
@@ -21,7 +21,7 @@ export interface Spec extends TurboModule {
21
21
  * @param instanceId - Unique ID for this engine instance (from createSTT)
22
22
  * @param modelDir - Absolute path to model directory
23
23
  * @param preferInt8 - Optional: true = prefer int8 models, false = prefer regular models, undefined = try int8 first (default)
24
- * @param modelType - Optional: explicit model type ('transducer', 'nemo_transducer', 'paraformer', 'nemo_ctc', 'wenet_ctc', 'sense_voice', 'zipformer_ctc', 'whisper', 'funasr_nano', 'fire_red_asr', 'moonshine', 'moonshine_v2', 'dolphin', 'canary', 'omnilingual', 'medasr', 'telespeech_ctc', 'auto'), undefined = auto (default)
24
+ * @param modelType - Optional: explicit model type ('transducer', 'nemo_transducer', 'paraformer', 'nemo_ctc', 'wenet_ctc', 'sense_voice', 'zipformer_ctc', 'whisper', 'funasr_nano', 'qwen3_asr', 'fire_red_asr', 'moonshine', 'moonshine_v2', 'dolphin', 'canary', 'omnilingual', 'medasr', 'telespeech_ctc', 'auto'), undefined = auto (default)
25
25
  * @param debug - Optional: enable debug logging in native layer and sherpa-onnx (default: false)
26
26
  * @param hotwordsFile - Optional: path to hotwords file (OfflineRecognizerConfig)
27
27
  * @param hotwordsScore - Optional: hotwords score (default in Kotlin 1.5)
@@ -30,10 +30,10 @@ export interface Spec extends TurboModule {
30
30
  * @param ruleFsts - Optional: path(s) to rule FSTs for ITN (comma-separated)
31
31
  * @param ruleFars - Optional: path(s) to rule FARs for ITN (comma-separated)
32
32
  * @param dither - Optional: dither for feature extraction. **Android:** applied. **iOS:** ignored (native API does not expose it)
33
- * @param modelOptions - Optional: model-specific options (whisper, senseVoice, canary, funasrNano). Only the block for the loaded model type is applied.
33
+ * @param modelOptions - Optional: model-specific options (whisper, senseVoice, canary, funasrNano, qwen3Asr). Only the block for the loaded model type is applied.
34
34
  * @param modelingUnit - Optional: 'cjkchar' | 'bpe' | 'cjkchar+bpe' for hotwords tokenization (OfflineModelConfig.modelingUnit)
35
35
  * @param bpeVocab - Optional: path to BPE vocab file (OfflineModelConfig.bpeVocab), used when modelingUnit is bpe or cjkchar+bpe
36
- * @returns Object with success boolean and array of detected models (each with type and modelDir)
36
+ * @returns Object with success boolean, array of detected models (each with type and modelDir), and optional error when success is false.
37
37
  */
38
38
  initializeStt(
39
39
  instanceId: string,
@@ -53,6 +53,8 @@ export interface Spec extends TurboModule {
53
53
  bpeVocab?: string
54
54
  ): Promise<{
55
55
  success: boolean;
56
+ /** Present when success is false (native structured failure). */
57
+ error?: string;
56
58
  detectedModels: Array<{ type: string; modelDir: string }>;
57
59
  modelType?: string;
58
60
  decodingMethod?: string;
@@ -131,6 +133,7 @@ export interface Spec extends TurboModule {
131
133
  * @param instanceId - Unique ID for this engine instance (from createStreamingSTT)
132
134
  * @param options - All init options (modelDir, modelType, enableEndpoint, decodingMethod, maxActivePaths, and optional endpoint/rule params).
133
135
  * `options.dither`: **Android** only; **iOS** ignores it (native `FeatureConfig` has no dither field).
136
+ * @returns `{ success: true }` on success, or `{ success: false, error?: string }` on structured native failure.
134
137
  */
135
138
  initializeOnlineSttWithOptions(
136
139
  instanceId: string,
@@ -160,7 +163,7 @@ export interface Spec extends TurboModule {
160
163
  rule3MinTrailingSilence?: number;
161
164
  rule3MinUtteranceLength?: number;
162
165
  }
163
- ): Promise<{ success: boolean }>;
166
+ ): Promise<{ success: boolean; error?: string }>;
164
167
 
165
168
  /** Create a new stream for the given OnlineRecognizer instance. */
166
169
  createSttStream(
@@ -238,7 +241,7 @@ export interface Spec extends TurboModule {
238
241
  * Initialize Text-to-Speech (TTS) with model directory.
239
242
  * @param instanceId - Unique ID for this engine instance (from createTTS)
240
243
  * @param modelDir - Absolute path to model directory
241
- * @param modelType - Model type ('vits', 'matcha', 'kokoro', 'kitten', 'pocket', 'zipvoice', 'auto')
244
+ * @param modelType - Model type ('vits', 'matcha', 'kokoro', 'kitten', 'pocket', 'zipvoice', 'supertonic', 'auto')
242
245
  * @param numThreads - Number of threads for inference (default: 2)
243
246
  * @param debug - Enable debug logging (default: false)
244
247
  * @param noiseScale - Optional noise scale (VITS/Matcha)
@@ -249,7 +252,7 @@ export interface Spec extends TurboModule {
249
252
  * @param maxNumSentences - Optional max sentences per callback (default: 1)
250
253
  * @param silenceScale - Optional silence scale on config (default: 0.2)
251
254
  * @param provider - Optional execution provider (e.g. 'cpu', 'coreml', 'xnnpack'; default: 'cpu')
252
- * @returns Object with success boolean and array of detected models (each with type and modelDir)
255
+ * @returns Object with success boolean, array of detected models (each with type and modelDir), sampleRate/numSpeakers on success, and optional error when success is false.
253
256
  */
254
257
  initializeTts(
255
258
  instanceId: string,
@@ -267,6 +270,8 @@ export interface Spec extends TurboModule {
267
270
  provider?: string
268
271
  ): Promise<{
269
272
  success: boolean;
273
+ /** Present when success is false (native structured failure). */
274
+ error?: string;
270
275
  detectedModels: Array<{ type: string; modelDir: string }>;
271
276
  sampleRate: number;
272
277
  numSpeakers: number;
@@ -299,7 +304,7 @@ export interface Spec extends TurboModule {
299
304
  * @param noiseScale - Optional noise scale override
300
305
  * @param noiseScaleW - Optional noise scale W override
301
306
  * @param lengthScale - Optional length scale override
302
- * @returns Object with success boolean and array of detected models
307
+ * @returns Object with success, detectedModels, sampleRate, numSpeakers on success, and optional error when success is false.
303
308
  */
304
309
  updateTtsParams(
305
310
  instanceId: string,
@@ -308,6 +313,8 @@ export interface Spec extends TurboModule {
308
313
  lengthScale?: number | null
309
314
  ): Promise<{
310
315
  success: boolean;
316
+ /** Present when success is false (native structured failure). */
317
+ error?: string;
311
318
  detectedModels: Array<{ type: string; modelDir: string }>;
312
319
  sampleRate: number;
313
320
  numSpeakers: number;
@@ -550,11 +557,18 @@ export interface Spec extends TurboModule {
550
557
  /**
551
558
  * Extract a .tar.bz2 archive to a target folder.
552
559
  * Returns { success, path } or { success, reason }.
560
+ *
561
+ * **Android:** When `showNotificationsEnabled` is true (default), a system notification shows
562
+ * extraction progress. Optional `notificationTitle` / `notificationText` customize the copy.
563
+ * **iOS:** Notification parameters are accepted but have no effect (no extraction progress notification).
553
564
  */
554
565
  extractTarBz2(
555
566
  sourcePath: string,
556
567
  targetPath: string,
557
- force: boolean
568
+ force: boolean,
569
+ showNotificationsEnabled?: boolean,
570
+ notificationTitle?: string,
571
+ notificationText?: string
558
572
  ): Promise<{
559
573
  success: boolean;
560
574
  path?: string;
@@ -570,11 +584,16 @@ export interface Spec extends TurboModule {
570
584
  /**
571
585
  * Extract a .tar.zst (or .zst) archive to a target folder.
572
586
  * Returns { success, path } or { success, reason }.
587
+ *
588
+ * **Android:** Same notification behavior as `extractTarBz2`. **iOS:** No effect.
573
589
  */
574
590
  extractTarZst(
575
591
  sourcePath: string,
576
592
  targetPath: string,
577
- force: boolean
593
+ force: boolean,
594
+ showNotificationsEnabled?: boolean,
595
+ notificationTitle?: string,
596
+ notificationText?: string
578
597
  ): Promise<{
579
598
  success: boolean;
580
599
  path?: string;
@@ -601,11 +620,15 @@ export interface Spec extends TurboModule {
601
620
  /**
602
621
  * Extract a .tar.zst archive from Android assets (AssetManager) to a target folder. Android only.
603
622
  * Streams from asset; no copy of the archive to disk. Used when PAD pack is APK_ASSETS.
623
+ * Notification options match `extractTarZst` (Android only).
604
624
  */
605
625
  extractTarZstFromAsset(
606
626
  assetPath: string,
607
627
  targetPath: string,
608
- force: boolean
628
+ force: boolean,
629
+ showNotificationsEnabled?: boolean,
630
+ notificationTitle?: string,
631
+ notificationText?: string
609
632
  ): Promise<{
610
633
  success: boolean;
611
634
  path?: string;
@@ -616,11 +639,15 @@ export interface Spec extends TurboModule {
616
639
  /**
617
640
  * Extract a .tar.bz2 archive from Android assets (AssetManager) to a target folder. Android only.
618
641
  * Streams from asset; no copy of the archive to disk. Used when PAD pack is APK_ASSETS.
642
+ * Notification options match `extractTarBz2` (Android only).
619
643
  */
620
644
  extractTarBz2FromAsset(
621
645
  assetPath: string,
622
646
  targetPath: string,
623
- force: boolean
647
+ force: boolean,
648
+ showNotificationsEnabled?: boolean,
649
+ notificationTitle?: string,
650
+ notificationText?: string
624
651
  ): Promise<{
625
652
  success: boolean;
626
653
  path?: string;
@@ -36,11 +36,13 @@ export {
36
36
  getDownloadStorageBase,
37
37
  } from './localModels';
38
38
  export {
39
+ configureModelDownloadBackgroundDownloader,
39
40
  downloadModelByCategory,
40
41
  getIncompleteDownloads,
41
42
  resumeDownload,
42
43
  deleteIncompleteDownload,
43
44
  } from './downloadTask';
45
+ export type { BackgroundDownloaderSetConfigOptions } from './downloadTask';
44
46
  export {
45
47
  extractModelByCategory,
46
48
  getIncompleteExtractions,
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Compile-time shim for @kesha-antonov/react-native-background-downloader.
3
+ *
4
+ * The package currently ships TS sources without distributable .d.ts files.
5
+ * We route TS path resolution to this shim so the SDK can typecheck strictly
6
+ * without checking third-party TS internals.
7
+ */
8
+
9
+ export interface BackgroundDownloaderNotificationTexts {
10
+ downloadTitle?: string;
11
+ downloadStarting?: string;
12
+ downloadProgress?: string;
13
+ downloadPaused?: string;
14
+ downloadFinished?: string;
15
+ groupTitle?: string;
16
+ groupText?: string | ((count: number) => string);
17
+ }
18
+
19
+ export type BackgroundDownloaderSetConfigOptions = {
20
+ showNotificationsEnabled?: boolean;
21
+ notificationsGrouping?: {
22
+ enabled?: boolean;
23
+ mode?: 'individual' | 'summaryOnly';
24
+ texts?: BackgroundDownloaderNotificationTexts;
25
+ };
26
+ headers?: Record<string, string>;
27
+ progressInterval?: number;
28
+ progressMinBytes?: number;
29
+ isLogsEnabled?: boolean;
30
+ maxParallelDownloads?: number;
31
+ allowsCellularAccess?: boolean;
32
+ };
33
+
34
+ export interface DownloadTask {
35
+ id: string;
36
+ start(): void;
37
+ stop(): void;
38
+ pause(): Promise<void>;
39
+ resume(): Promise<void>;
40
+ begin(
41
+ cb: (data: {
42
+ expectedBytes?: number;
43
+ headers?: Record<string, string>;
44
+ }) => void
45
+ ): DownloadTask;
46
+ progress(
47
+ cb: (data: { bytesDownloaded: number; bytesTotal: number }) => void
48
+ ): DownloadTask;
49
+ done(
50
+ cb: (data: {
51
+ location?: string;
52
+ bytesDownloaded: number;
53
+ bytesTotal: number;
54
+ }) => void
55
+ ): DownloadTask;
56
+ error(
57
+ cb: (data: { error?: string; errorCode?: number }) => void
58
+ ): DownloadTask;
59
+ }
60
+
61
+ export declare function setConfig(
62
+ options: BackgroundDownloaderSetConfigOptions
63
+ ): void;
64
+
65
+ export declare function createDownloadTask(options: {
66
+ id: string;
67
+ url: string;
68
+ destination: string;
69
+ metadata?: Record<string, unknown>;
70
+ }): DownloadTask;
71
+
72
+ export declare function completeHandler(taskId: string): void;
73
+ export declare function getExistingDownloadTasks(): Promise<DownloadTask[]>;
@@ -1,7 +1,10 @@
1
+ import { Platform } from 'react-native';
2
+ import type { BackgroundDownloaderSetConfigOptions } from './background-downloader-types';
1
3
  import {
2
4
  createDownloadTask,
3
5
  completeHandler,
4
6
  getExistingDownloadTasks,
7
+ setConfig,
5
8
  } from '@kesha-antonov/react-native-background-downloader';
6
9
  import {
7
10
  exists,
@@ -41,6 +44,68 @@ function makeDownloadTaskId(category: ModelCategory, id: string): string {
41
44
 
42
45
  const activeDownloadTasks = new Map<string, { stop: () => void }>();
43
46
 
47
+ let androidDownloaderNotificationConfigApplied = false;
48
+ let didWarnConfigFailure = false;
49
+
50
+ function warnBackgroundDownloaderConfigFailure(
51
+ context: string,
52
+ error: unknown
53
+ ) {
54
+ if (didWarnConfigFailure) return;
55
+ didWarnConfigFailure = true;
56
+ const reason = error instanceof Error ? error.message : String(error);
57
+ console.warn(
58
+ `[Download] Background downloader config failed (${context}): ${reason}`
59
+ );
60
+ }
61
+
62
+ export type { BackgroundDownloaderSetConfigOptions };
63
+
64
+ /**
65
+ * Apply your own `@kesha-antonov/react-native-background-downloader` `setConfig` **before** the first
66
+ * model download. When called, the SDK will **not** overwrite it with built-in defaults on first download.
67
+ *
68
+ * Safe to call at app startup (e.g. `App.tsx`). Other `setConfig` options (e.g. headers) are forwarded
69
+ * where the native module supports them on each platform.
70
+ */
71
+ export function configureModelDownloadBackgroundDownloader(
72
+ options: BackgroundDownloaderSetConfigOptions
73
+ ): void {
74
+ try {
75
+ setConfig(options);
76
+ androidDownloaderNotificationConfigApplied = true;
77
+ } catch (error) {
78
+ // Keep fallback default config enabled if custom config fails.
79
+ warnBackgroundDownloaderConfigFailure('custom', error);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Library default is showNotificationsEnabled: false (silent empty FGS notification).
85
+ * Enable visible notifications unless the host app already called `configureModelDownloadBackgroundDownloader`.
86
+ */
87
+ function ensureAndroidBackgroundDownloaderNotifications() {
88
+ if (androidDownloaderNotificationConfigApplied) return;
89
+ if (Platform.OS !== 'android') return;
90
+ try {
91
+ setConfig({
92
+ showNotificationsEnabled: true,
93
+ notificationsGrouping: {
94
+ enabled: false,
95
+ mode: 'individual',
96
+ texts: {
97
+ downloadTitle: 'Model download',
98
+ downloadStarting: 'Starting download…',
99
+ downloadProgress: 'Downloading… {progress}%',
100
+ },
101
+ },
102
+ });
103
+ androidDownloaderNotificationConfigApplied = true;
104
+ } catch (error) {
105
+ warnBackgroundDownloaderConfigFailure('default', error);
106
+ }
107
+ }
108
+
44
109
  export async function downloadModelByCategory<T extends ModelMetaBase>(
45
110
  category: ModelCategory,
46
111
  id: string,
@@ -103,6 +168,8 @@ export async function downloadModelByCategory<T extends ModelMetaBase>(
103
168
  throw new Error(`Insufficient disk space: ${diskSpaceCheck.message}`);
104
169
  }
105
170
 
171
+ ensureAndroidBackgroundDownloaderNotifications();
172
+
106
173
  const statePath = getDownloadStatePath(category, id);
107
174
 
108
175
  if (opts?.overwrite) {
@@ -339,6 +406,7 @@ export async function resumeDownload<T extends ModelMetaBase>(
339
406
  deleteArchiveAfterExtract?: boolean;
340
407
  }
341
408
  ): Promise<DownloadResult> {
409
+ ensureAndroidBackgroundDownloaderNotifications();
342
410
  const taskId = makeDownloadTaskId(category, id);
343
411
  const existingTasks = await getExistingDownloadTasks();
344
412
  const existing = existingTasks.find((t) => t.id === taskId);
@@ -30,8 +30,10 @@ export {
30
30
  ModelCategory,
31
31
  getProtectedModelKeysForBulkDelete,
32
32
  purgeDownloadedModelArtifacts,
33
+ configureModelDownloadBackgroundDownloader,
33
34
  } from './ModelDownloadManager';
34
35
  export type {
36
+ BackgroundDownloaderSetConfigOptions,
35
37
  ModelMetaBase,
36
38
  TtsModelMeta,
37
39
  TtsModelType,
@@ -43,6 +43,15 @@ export type RunPostDownloadProcessingOptions = {
43
43
  onChecksumIssue?: (issue: ChecksumIssue) => Promise<boolean>;
44
44
  deleteArchiveAfterExtract?: boolean;
45
45
  onProgress?: (progress: DownloadProgress) => void;
46
+ /**
47
+ * **Android:** Native extraction progress notification (default true), aligned with the download-manager flow.
48
+ * **iOS:** No effect.
49
+ */
50
+ showExtractionNotifications?: boolean;
51
+ /** **Android:** Optional notification title (default: SDK/native default title). */
52
+ extractionNotificationTitle?: string;
53
+ /** **Android:** Optional notification body prefix (progress percent is appended natively). */
54
+ extractionNotificationText?: string;
46
55
  /** Called to get current list of downloaded models for emitModelsListUpdated. */
47
56
  getDownloadedList: () => Promise<ModelMetaBase[]>;
48
57
  };
@@ -63,6 +72,9 @@ export async function runPostDownloadProcessing(
63
72
  deleteArchiveAfterExtract,
64
73
  onProgress,
65
74
  getDownloadedList,
75
+ showExtractionNotifications,
76
+ extractionNotificationTitle,
77
+ extractionNotificationText,
66
78
  } = options;
67
79
 
68
80
  registerActivePostProcess(category, id);
@@ -80,6 +92,9 @@ export async function runPostDownloadProcessing(
80
92
  deleteArchiveAfterExtract,
81
93
  onProgress,
82
94
  getDownloadedList,
95
+ showExtractionNotifications,
96
+ extractionNotificationTitle,
97
+ extractionNotificationText,
83
98
  });
84
99
  } finally {
85
100
  unregisterActivePostProcess(category, id);
@@ -102,6 +117,9 @@ async function runPostDownloadProcessingBody(
102
117
  deleteArchiveAfterExtract,
103
118
  onProgress,
104
119
  getDownloadedList,
120
+ showExtractionNotifications,
121
+ extractionNotificationTitle,
122
+ extractionNotificationText,
105
123
  } = options;
106
124
 
107
125
  const isAborted = () => Boolean(signal?.aborted);
@@ -164,7 +182,12 @@ async function runPostDownloadProcessingBody(
164
182
  onProgress?.(progress);
165
183
  emitDownloadProgress(category, id, progress);
166
184
  },
167
- signal
185
+ signal,
186
+ {
187
+ showNotificationsEnabled: showExtractionNotifications !== false,
188
+ notificationTitle: extractionNotificationTitle,
189
+ notificationText: extractionNotificationText,
190
+ }
168
191
  );
169
192
  }
170
193
 
@@ -96,6 +96,7 @@ function deriveType(id: string): TtsModelType {
96
96
  if (lower.includes('kitten')) return 'kitten';
97
97
  if (lower.includes('pocket')) return 'pocket';
98
98
  if (lower.includes('zipvoice')) return 'zipvoice';
99
+ if (lower.includes('supertonic')) return 'supertonic';
99
100
  return 'unknown';
100
101
  }
101
102
 
@@ -1,5 +1,6 @@
1
1
  import { DeviceEventEmitter } from 'react-native';
2
2
  import SherpaOnnx from '../NativeSherpaOnnx';
3
+ import type { ExtractNotificationArgs } from './types';
3
4
 
4
5
  export type ExtractProgressEvent = {
5
6
  bytes: number;
@@ -19,7 +20,8 @@ export async function extractTarBz2(
19
20
  targetPath: string,
20
21
  force = true,
21
22
  onProgress?: (event: ExtractProgressEvent) => void,
22
- signal?: AbortSignal
23
+ signal?: AbortSignal,
24
+ notification?: ExtractNotificationArgs
23
25
  ): Promise<ExtractResult> {
24
26
  let subscription: { remove: () => void } | null = null;
25
27
  let removeAbortListener: (() => void) | null = null;
@@ -62,7 +64,10 @@ export async function extractTarBz2(
62
64
  const result = await SherpaOnnx.extractTarBz2(
63
65
  sourcePath,
64
66
  targetPath,
65
- force
67
+ force,
68
+ notification?.showNotificationsEnabled,
69
+ notification?.notificationTitle,
70
+ notification?.notificationText
66
71
  );
67
72
  if (!result.success) {
68
73
  const message = result.reason || 'Extraction failed';
@@ -1,5 +1,6 @@
1
1
  import { DeviceEventEmitter } from 'react-native';
2
2
  import SherpaOnnx from '../NativeSherpaOnnx';
3
+ import type { ExtractNotificationArgs } from './types';
3
4
 
4
5
  export type ExtractProgressEvent = {
5
6
  bytes: number;
@@ -19,7 +20,8 @@ export async function extractTarZst(
19
20
  targetPath: string,
20
21
  force = true,
21
22
  onProgress?: (event: ExtractProgressEvent) => void,
22
- signal?: AbortSignal
23
+ signal?: AbortSignal,
24
+ notification?: ExtractNotificationArgs
23
25
  ): Promise<ExtractResult> {
24
26
  let subscription: { remove: () => void } | null = null;
25
27
  let removeAbortListener: (() => void) | null = null;
@@ -61,7 +63,10 @@ export async function extractTarZst(
61
63
  const result = await SherpaOnnx.extractTarZst(
62
64
  sourcePath,
63
65
  targetPath,
64
- force
66
+ force,
67
+ notification?.showNotificationsEnabled,
68
+ notification?.notificationTitle,
69
+ notification?.notificationText
65
70
  );
66
71
  if (!result.success) {
67
72
  const message = result.reason || 'Extraction failed';