react-native-audio-browser 0.2.0-next.17 → 0.2.0-next.19

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 (81) hide show
  1. package/android/build.gradle +2 -1
  2. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +160 -106
  3. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +11 -29
  4. package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
  5. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
  6. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +377 -110
  7. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +13 -1
  8. package/android/src/main/res/values/strings.xml +6 -21
  9. package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
  10. package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
  11. package/lib/commonjs/features/browser.js +8 -0
  12. package/lib/commonjs/features/browser.js.map +1 -1
  13. package/lib/commonjs/web/NativeAudioBrowser.js +163 -50
  14. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  15. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
  16. package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
  17. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  18. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  19. package/lib/commonjs/web/TrackPlayer/Player.js +232 -39
  20. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  21. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
  22. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
  23. package/lib/commonjs/web/browser/BrowserManager.js +30 -12
  24. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  25. package/lib/commonjs/web/browser/SearchManager.js +16 -19
  26. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  27. package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
  28. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  29. package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
  30. package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
  31. package/lib/module/features/browser.js +8 -0
  32. package/lib/module/features/browser.js.map +1 -1
  33. package/lib/module/web/NativeAudioBrowser.js +163 -50
  34. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  35. package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
  36. package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
  37. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
  38. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
  39. package/lib/module/web/TrackPlayer/Player.js +232 -39
  40. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  41. package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
  42. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
  43. package/lib/module/web/browser/BrowserManager.js +30 -12
  44. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  45. package/lib/module/web/browser/SearchManager.js +16 -19
  46. package/lib/module/web/browser/SearchManager.js.map +1 -1
  47. package/lib/module/web/http/RequestConfigBuilder.js +110 -27
  48. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  49. package/lib/module/web/http/followMediaRedirect.js +215 -0
  50. package/lib/module/web/http/followMediaRedirect.js.map +1 -0
  51. package/lib/typescript/src/features/browser.d.ts +8 -0
  52. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  53. package/lib/typescript/src/types/browser.d.ts +13 -4
  54. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  55. package/package.json +1 -1
  56. package/src/features/browser.ts +8 -0
  57. package/src/types/browser.ts +13 -4
  58. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
  59. package/src/web/NativeAudioBrowser.ts +193 -60
  60. package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
  61. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
  62. package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
  63. package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
  64. package/src/web/TrackPlayer/Player.load.test.ts +137 -1
  65. package/src/web/TrackPlayer/Player.ts +256 -37
  66. package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
  67. package/src/web/__tests__/lifecycle.test.ts +345 -0
  68. package/src/web/__tests__/lifecycleHarness.ts +351 -0
  69. package/src/web/__tests__/loadEdgeCases.test.ts +195 -0
  70. package/src/web/__tests__/loadSupersession.test.ts +175 -0
  71. package/src/web/__tests__/retry.test.ts +169 -0
  72. package/src/web/browser/BrowserManager.ts +32 -15
  73. package/src/web/browser/SearchManager.test.ts +75 -0
  74. package/src/web/browser/SearchManager.ts +23 -26
  75. package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
  76. package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
  77. package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
  78. package/src/web/http/RequestConfigBuilder.test.ts +241 -1
  79. package/src/web/http/RequestConfigBuilder.ts +140 -33
  80. package/src/web/http/followMediaRedirect.test.ts +289 -0
  81. package/src/web/http/followMediaRedirect.ts +253 -0
@@ -92,8 +92,16 @@ export declare function hasSearch(): boolean;
92
92
  /**
93
93
  * Searches for tracks using the configured search source.
94
94
  *
95
+ * Resolves `[]` only when the source found nothing; a failed search rejects,
96
+ * so a caller can tell "no results" from "the request failed". The rejection's
97
+ * message describes the failure — native platforms reject with the underlying
98
+ * throwable and JS sees only its message. On web the `Error` also carries a
99
+ * `code`, the {@link NavigationErrorType} the browse path reports as a
100
+ * {@link NavigationError} (`'http-error'`, `'network-error'`).
101
+ *
95
102
  * @param query - The search query string
96
103
  * @returns Promise resolving to an array of matching tracks
104
+ * @throws When the search source or its request fails
97
105
  *
98
106
  * @see {@link configureBrowser}
99
107
  *
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../../src/features/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAUhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,oBAAoB,GAAG,IAAI,CAO1E;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,GAAG,SAAS,CAE1E;AAED,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,KAAK,QAMnD;AAED,wBAAgB,OAAO,uBAEtB;AAED,eAAO,MAAM,aAAa,wCAEzB,CAAA;AAED,wBAAgB,OAAO,IAAI,MAAM,GAAG,SAAS,CAE5C;AAED,wBAAgB,UAAU,IAAI,aAAa,GAAG,SAAS,CAEtD;AAED,eAAO,MAAM,gBAAgB,+CAEmB,CAAA;AAEhD,wBAAgB,UAAU,IAAI,aAAa,GAAG,SAAS,CAEtD;AACD,wBAAgB,OAAO,IAAI,KAAK,EAAE,GAAG,SAAS,CAE7C;AAED,eAAO,MAAM,aAAa,yCAEzB,CAAA;AAED,wBAAgB,OAAO,IAAI,KAAK,EAAE,GAAG,SAAS,CAE7C;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,SAAU,MAAM,KAAG,IAEnD,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,QAAO,IAEvC,CAAA;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAInC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAE5D"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../../src/features/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAUhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,oBAAoB,GAAG,IAAI,CAO1E;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,GAAG,SAAS,CAE1E;AAED,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,KAAK,QAMnD;AAED,wBAAgB,OAAO,uBAEtB;AAED,eAAO,MAAM,aAAa,wCAEzB,CAAA;AAED,wBAAgB,OAAO,IAAI,MAAM,GAAG,SAAS,CAE5C;AAED,wBAAgB,UAAU,IAAI,aAAa,GAAG,SAAS,CAEtD;AAED,eAAO,MAAM,gBAAgB,+CAEmB,CAAA;AAEhD,wBAAgB,UAAU,IAAI,aAAa,GAAG,SAAS,CAEtD;AACD,wBAAgB,OAAO,IAAI,KAAK,EAAE,GAAG,SAAS,CAE7C;AAED,eAAO,MAAM,aAAa,yCAEzB,CAAA;AAED,wBAAgB,OAAO,IAAI,KAAK,EAAE,GAAG,SAAS,CAE7C;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,SAAU,MAAM,KAAG,IAEnD,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,QAAO,IAEvC,CAAA;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAInC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAE5D"}
@@ -372,8 +372,16 @@ export interface ImageQueryParams {
372
372
  * When a request is made, configs are merged in this order (later overrides earlier):
373
373
  * 1. `request` (base config) - shared settings like user agent, common headers
374
374
  * 2. `media`/`artwork` config - resource-specific settings
375
- * 3. `resolve(track)` result - per-track overrides (if provided)
376
- * 4. `transform(request)` result - final modifications (if provided)
375
+ * 3. the per-track callbacks, in an order that differs by kind:
376
+ * - `artwork`: `resolve(track)`, then `transform(request)` last
377
+ * - `media`: `transform(request)`, then **`resolve(track)` last** — the
378
+ * per-track resolver is the most specific layer and wins over the
379
+ * transform, on every platform
380
+ *
381
+ * The `media` order is deliberate: a resolver minting a per-track signed URL
382
+ * must not be overwritten by the static layer that shaped the unsigned one. It
383
+ * means a `media.transform` cannot sign what `resolve` produced — do that work
384
+ * inside `resolve`, or move the signing to `transform` and drop `resolve`.
377
385
  *
378
386
  * ### Usage Patterns
379
387
  *
@@ -418,8 +426,9 @@ export interface MediaRequestConfig extends TransformableRequestConfig {
418
426
  * Receives the full Track object, allowing URL generation based on
419
427
  * track metadata (id, artist, album, src, etc.).
420
428
  *
421
- * The returned config is merged with base configs, then passed to
422
- * `transform` if provided.
429
+ * Runs **last** for media — after `transform`, and winning over it on every
430
+ * field including `path`. See the hierarchy note on this interface; artwork
431
+ * runs them the other way round.
423
432
  *
424
433
  * @param track - The track being requested
425
434
  * @returns Request configuration for this specific track (sync or async)
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../../src/types/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE9D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,KAAK,EAAE,KAAK,CAAA;IACZ,mCAAmC;IACnC,KAAK,EAAE,KAAK,EAAE,CAAA;IACd,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE9E,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,WAAW,CAAA;AAEtD,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,0BAA0B,KAC9B,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,UAAU,GAClB,KAAK,GACL,MAAM,GACN,UAAU,GACV,SAAS,GACT,SAAS,GACT,WAAW,GACX,MAAM,GACN,OAAO,GACP,aAAa,GACb,OAAO,GACP,SAAS,GACT,iBAAiB,CAAA;AAErB;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,CAAA;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;OAUG;IACH,SAAS,EAAE,cAAc,CAAA;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;AAO7E,MAAM,MAAM,wBAAwB,GAAG,CACrC,OAAO,EAAE,aAAa,EACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,OAAO,CAAC,aAAa,CAAC,CAAA;AAC3B,MAAM,MAAM,4BAA4B,GAAG,CACzC,OAAO,EAAE,aAAa,EACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,aAAa,CAAA;AAElB,MAAM,MAAM,UAAU,GAClB,KAAK,GACL,MAAM,GACN,KAAK,GACL,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,CAAA;AAEb,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,OAAO,EAAE,aAAa,CAAA;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,MAAM,6BAA6B,GAAG,CAC1C,MAAM,EAAE,oBAAoB,KACzB,OAAO,CAAC,aAAa,CAAC,CAAA;AAC3B,MAAM,MAAM,iCAAiC,GAAG,CAC9C,MAAM,EAAE,oBAAoB,KACzB,aAAa,CAAA;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,0BAA2B,SAAQ,aAAa;IAC/D,mFAAmF;IACnF,SAAS,CAAC,EAAE,wBAAwB,CAAA;IACpC,qGAAqG;IACrG,aAAa,CAAC,EAAE,4BAA4B,CAAA;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAChC,0BAA0B,GAC1B,OAAO,CAAC,0BAA0B,CAAC,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,WAAW,kBAAmB,SAAQ,0BAA0B;IACpE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,yFAAyF;IACzF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;IAClD,2GAA2G;IAC3G,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,aAAa,CAAA;CAC9C;AAED,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,yFAAyF;IACzF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;IAClD,2GAA2G;IAC3G,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,aAAa,CAAA;IAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,6EAA6E;IAC7E,SAAS,CAAC,EAAE,6BAA6B,CAAA;IACzC,+FAA+F;IAC/F,aAAa,CAAC,EAAE,iCAAiC,CAAA;IAEjD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;CACpC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GACrB,aAAa,GACb,qBAAqB,GACrB,0BAA0B,CAAA;AAE9B,MAAM,MAAM,WAAW,GAAG,qBAAqB,GAAG,0BAA0B,CAAA;AAE5E;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,kBAAkB,CAAA;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,oBAAoB,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;AACjE;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB,KAAK,EAAE,GACP,kBAAkB,GAClB,0BAA0B,CAAA;AAE9B;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG,0BAA0B,CAAA;AAE5E,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAIzB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,0BAA0B,GAAG,qBAAqB,CAAA;IAI5D;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,EAAE,0BAA0B,GAAG,qBAAqB,CAAA;IAE3D;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,kBAAkB,CAAA;IAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,OAAO,CAAC,EAAE,oBAAoB,CAAA;IAE9B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,iBAAiB,CAAC,EAAE,oBAAoB,CAAA;IAIxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAA;IAErB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,UAAU,CAAA;IAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4CG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC,CAAA;IAIpD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,uBAAuB,CAAA;IAEzC;;;;;;;;;;;OAWG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAA;IAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAA;IAErC;;;;;;;;;;;;;;;OAeG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAE5B;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,EAAE,wBAAwB,CAAA;IAE3C;;;;;;;;;;;;;;;;;;OAkBG;IACH,qBAAqB,CAAC,EAAE,6BAA6B,CAAA;CACtD,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,QAAQ,GACR,UAAU,GACV,eAAe,CAAA;AAEnB;;;;;;;;GAQG;AACH,MAAM,MAAM,+BAA+B,GAAG,SAAS,GAAG,UAAU,CAAA;AAEpE;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,GAAG,KAAK,GAAG,QAAQ,CAAA;AAE5D;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,yCAAyC;IACzC,KAAK,EAAE,eAAe,CAAA;IACtB,mEAAmE;IACnE,gBAAgB,EAAE,wBAAwB,CAAA;IAC1C,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAC1C,MAAM,EAAE,2BAA2B,KAChC,wBAAwB,GAAG,SAAS,CAAA;AAEzC;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,SAAS,CAAA;AAE3E,+EAA+E;AAC/E,MAAM,MAAM,qBAAqB,GAAG,MAAM,MAAM,CAAA"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../../src/types/browser.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-browser",
3
- "version": "0.2.0-next.17",
3
+ "version": "0.2.0-next.19",
4
4
  "description": "react-native-audio-browser is a react native package built with Nitro",
5
5
  "keywords": [
6
6
  "react-native",
@@ -157,8 +157,16 @@ export function hasSearch(): boolean {
157
157
  /**
158
158
  * Searches for tracks using the configured search source.
159
159
  *
160
+ * Resolves `[]` only when the source found nothing; a failed search rejects,
161
+ * so a caller can tell "no results" from "the request failed". The rejection's
162
+ * message describes the failure — native platforms reject with the underlying
163
+ * throwable and JS sees only its message. On web the `Error` also carries a
164
+ * `code`, the {@link NavigationErrorType} the browse path reports as a
165
+ * {@link NavigationError} (`'http-error'`, `'network-error'`).
166
+ *
160
167
  * @param query - The search query string
161
168
  * @returns Promise resolving to an array of matching tracks
169
+ * @throws When the search source or its request fails
162
170
  *
163
171
  * @see {@link configureBrowser}
164
172
  *
@@ -432,8 +432,16 @@ export interface ImageQueryParams {
432
432
  * When a request is made, configs are merged in this order (later overrides earlier):
433
433
  * 1. `request` (base config) - shared settings like user agent, common headers
434
434
  * 2. `media`/`artwork` config - resource-specific settings
435
- * 3. `resolve(track)` result - per-track overrides (if provided)
436
- * 4. `transform(request)` result - final modifications (if provided)
435
+ * 3. the per-track callbacks, in an order that differs by kind:
436
+ * - `artwork`: `resolve(track)`, then `transform(request)` last
437
+ * - `media`: `transform(request)`, then **`resolve(track)` last** — the
438
+ * per-track resolver is the most specific layer and wins over the
439
+ * transform, on every platform
440
+ *
441
+ * The `media` order is deliberate: a resolver minting a per-track signed URL
442
+ * must not be overwritten by the static layer that shaped the unsigned one. It
443
+ * means a `media.transform` cannot sign what `resolve` produced — do that work
444
+ * inside `resolve`, or move the signing to `transform` and drop `resolve`.
437
445
  *
438
446
  * ### Usage Patterns
439
447
  *
@@ -478,8 +486,9 @@ export interface MediaRequestConfig extends TransformableRequestConfig {
478
486
  * Receives the full Track object, allowing URL generation based on
479
487
  * track metadata (id, artist, album, src, etc.).
480
488
  *
481
- * The returned config is merged with base configs, then passed to
482
- * `transform` if provided.
489
+ * Runs **last** for media — after `transform`, and winning over it on every
490
+ * field including `path`. See the hierarchy note on this interface; artwork
491
+ * runs them the other way round.
483
492
  *
484
493
  * @param track - The track being requested
485
494
  * @returns Request configuration for this specific track (sync or async)
@@ -156,9 +156,14 @@ describe('NativeAudioBrowser stop vs in-flight load', () => {
156
156
  await new Promise((resolve) => setTimeout(resolve, 0))
157
157
 
158
158
  // The stale load must not revive the player: previously its continuation
159
- // re-armed _isStopped = false and set the current track after stop().
160
- expect(browser.current).toBeUndefined()
159
+ // re-armed _isStopped = false and re-entered super.load() after stop().
161
160
  expect(browser.getPlayback().state).toBe('stopped')
161
+ // `current` is the track, not undefined: the transition is announced when
162
+ // load() is called, before the URL resolves, so by the time stop() runs the
163
+ // track is already active — and `Player.stop()` deliberately keeps it so
164
+ // play() can resume. Android behaves the same way; it was only undefined
165
+ // here because the async gap meant it had never been set.
166
+ expect(browser.current?.id).toBe(track.id)
162
167
  })
163
168
  })
164
169
 
@@ -299,3 +304,228 @@ describe('NativeAudioBrowser setQueue start position', () => {
299
304
  expect(skips).toEqual([[0, 30]])
300
305
  })
301
306
  })
307
+
308
+ /**
309
+ * Android emits the active-track transition from ExoPlayer's
310
+ * `onMediaItemTransition`, which fires when the media item is set — before the
311
+ * load has resolved, and regardless of whether it ever does. iOS drives it from
312
+ * the queue coordinator, which behaves the same. Web announcing it only on
313
+ * success meant a track that failed to load never became the active track for
314
+ * consumers, so a UI bound to `useActiveTrack()` had nothing to render and a
315
+ * playback error had nowhere to appear.
316
+ */
317
+ describe('NativeAudioBrowser active track announced on the load attempt', () => {
318
+ class FailingLoadBrowser extends TestBrowser {
319
+ constructor(loadResult: () => Promise<void>) {
320
+ super()
321
+ this.player = {
322
+ load: loadResult,
323
+ unload: () => Promise.resolve()
324
+ } as unknown as typeof this.player
325
+ }
326
+
327
+ /** getNowPlaying() reads progress off the element, which needs buffered ranges. */
328
+ withProgressCapableElement(): this {
329
+ this.element = {
330
+ play: () => Promise.resolve(),
331
+ pause: () => {},
332
+ currentTime: 0,
333
+ duration: 100,
334
+ buffered: { length: 0, end: () => 0 }
335
+ } as unknown as HTMLMediaElement
336
+ return this
337
+ }
338
+ }
339
+
340
+ const tick = () => new Promise((resolve) => setTimeout(resolve, 0))
341
+
342
+ it('announces the transition even when the load fails', async () => {
343
+ const browser = new FailingLoadBrowser(() =>
344
+ Promise.reject(new Error('401'))
345
+ )
346
+ const announced: (Track | undefined)[] = []
347
+ browser.onPlaybackActiveTrackChanged = (event) =>
348
+ announced.push(event.track)
349
+
350
+ browser.load(track)
351
+ await tick()
352
+
353
+ expect(announced.map((t) => t?.src)).toContain(track.src)
354
+ expect(browser.getActiveTrack()?.src).toBe(track.src)
355
+ })
356
+
357
+ it('announces the transition exactly once on a successful load', async () => {
358
+ const browser = new FailingLoadBrowser(() => Promise.resolve())
359
+ const announced: (Track | undefined)[] = []
360
+ browser.onPlaybackActiveTrackChanged = (event) =>
361
+ announced.push(event.track)
362
+
363
+ browser.load(track)
364
+ await tick()
365
+
366
+ expect(announced).toHaveLength(1)
367
+ expect(announced[0]?.src).toBe(track.src)
368
+ })
369
+
370
+ it('does not report a load failure when the announcement throws', async () => {
371
+ // The announcement runs after the load has committed, so a consumer's own
372
+ // throwing handler must not travel back up and error a load that worked.
373
+ const browser = new FailingLoadBrowser(() => Promise.resolve())
374
+ browser.onPlaybackActiveTrackChanged = () => {
375
+ throw new Error('consumer handler blew up')
376
+ }
377
+ const errors: (string | undefined)[] = []
378
+ browser.onPlaybackError = (event) => errors.push(event.error?.code)
379
+
380
+ browser.load(track)
381
+ await tick()
382
+
383
+ expect(errors).not.toContain('load-error')
384
+ expect(browser.getPlayback().state).not.toBe('error')
385
+ })
386
+
387
+ it('announces the transition before the error for a track with no src', async () => {
388
+ // super.load() sets the error state synchronously when there is nothing to
389
+ // play, so announcing after it put onPlaybackError first — and a consumer
390
+ // that clears its banner on a track change wiped the error. Native cannot
391
+ // invert these: Android fires onMediaItemTransition at prepare, onPlayerError
392
+ // only after.
393
+ const browser = new FailingLoadBrowser(() => Promise.resolve())
394
+ const order: string[] = []
395
+ browser.onPlaybackActiveTrackChanged = () => order.push('track-changed')
396
+ browser.onPlaybackError = (event) => {
397
+ if (event.error) order.push('error')
398
+ }
399
+
400
+ browser.load({ id: 'no-src', title: 'No src' })
401
+ await tick()
402
+
403
+ expect(order).toEqual(['track-changed', 'error'])
404
+ })
405
+
406
+ it('does not report loading before the active track change', async () => {
407
+ // Native emits these in the opposite order — Android's active-track
408
+ // callback comes from onMediaItemTransition, its LOADING from onEvents,
409
+ // which ExoPlayer runs after the individual callbacks. Dispatching
410
+ // `trackLoading` when load() is called inverted that, handing a consumer
411
+ // that reads getActiveTrack() on the loading edge the outgoing track.
412
+ const browser = new FailingLoadBrowser(() => Promise.resolve())
413
+ const order: string[] = []
414
+ browser.onPlaybackChanged = (playback) => {
415
+ if (playback.state === 'loading') order.push('loading')
416
+ }
417
+ browser.onPlaybackActiveTrackChanged = () => order.push('active-track')
418
+
419
+ browser.load(track)
420
+ // both land synchronously, in native's order: the transition is announced
421
+ // when the caller asks for it, and `loading` covers the resolution that
422
+ // follows
423
+ expect(order).toEqual(['active-track', 'loading'])
424
+
425
+ await tick()
426
+ expect(order.indexOf('active-track')).toBeLessThan(order.indexOf('loading'))
427
+ })
428
+
429
+ it('still publishes now-playing when the announcement throws', async () => {
430
+ // The two are independent: a consumer's throwing handler must not leave the
431
+ // lock screen showing the previous track.
432
+ const browser = new FailingLoadBrowser(() =>
433
+ Promise.resolve()
434
+ ).withProgressCapableElement()
435
+ browser.onPlaybackActiveTrackChanged = () => {
436
+ throw new Error('consumer handler blew up')
437
+ }
438
+ const published: (string | undefined)[] = []
439
+ browser.onNowPlayingChanged = (metadata) => published.push(metadata.title)
440
+
441
+ browser.load(track)
442
+ await tick()
443
+
444
+ expect(published).toContain(track.title)
445
+ })
446
+ })
447
+
448
+ /**
449
+ * The media path reads the *resolved* request layer, so unlike browse it can be
450
+ * handed a rejected resolver. A track whose `src` is already absolute needs no
451
+ * layer at all, and native degrades rather than failing — `getMediaRequestConfig`
452
+ * returns null and the original URL plays on.
453
+ */
454
+ describe('NativeAudioBrowser media load vs a failing request resolver', () => {
455
+ const tick = () => new Promise((resolve) => setTimeout(resolve, 0))
456
+
457
+ it('still plays an absolute src when the request resolver rejects', async () => {
458
+ const browser = new TestBrowser()
459
+ browser.configuration = {
460
+ requestResolver: () => Promise.reject(new Error('token refresh failed'))
461
+ }
462
+ const errors: (string | undefined)[] = []
463
+ browser.onPlaybackError = (event) => errors.push(event.error?.code)
464
+
465
+ browser.load(track)
466
+ await tick()
467
+
468
+ expect(errors).not.toContain('load-error')
469
+ expect(browser.getActiveTrack()?.src).toBe(track.src)
470
+ })
471
+ })
472
+
473
+ /**
474
+ * Both native platforms clear the playback error when the state leaves `error`,
475
+ * and say so: Android emits `onPlaybackError(null)` when leaving ERROR, iOS
476
+ * clears `playbackError` before emitting the state change. Web derived the
477
+ * cleared value correctly from state but never emitted it, so a consumer
478
+ * subscribed to the event kept showing a stale error after the next track
479
+ * loaded fine.
480
+ */
481
+ describe('NativeAudioBrowser playback error lifecycle', () => {
482
+ class ErrorStateBrowser extends TestBrowser {
483
+ setErrorState(code: string): void {
484
+ this.state = {
485
+ state: 'error',
486
+ error: { kind: 'unknown', code, message: code }
487
+ }
488
+ }
489
+
490
+ setPlainState(state: PlaybackState): void {
491
+ this.state = { state }
492
+ }
493
+ }
494
+
495
+ const collect = () => {
496
+ const browser = new ErrorStateBrowser()
497
+ const events: (string | undefined)[] = []
498
+ browser.onPlaybackError = (event) => events.push(event.error?.code)
499
+ return { browser, events }
500
+ }
501
+
502
+ it('emits the error, then emits the clear when leaving the error state', () => {
503
+ const { browser, events } = collect()
504
+
505
+ browser.setErrorState('shaka-1001')
506
+ expect(events).toEqual(['shaka-1001'])
507
+ expect(browser.getPlaybackError()?.code).toBe('shaka-1001')
508
+
509
+ browser.setPlainState('loading')
510
+ expect(events).toEqual(['shaka-1001', undefined])
511
+ expect(browser.getPlaybackError()).toBeUndefined()
512
+ })
513
+
514
+ it('does not emit a clear when no error preceded the transition', () => {
515
+ const { browser, events } = collect()
516
+
517
+ browser.setPlainState('loading')
518
+ browser.setPlainState('playing')
519
+
520
+ expect(events).toEqual([])
521
+ })
522
+
523
+ it('emits the new error when one error replaces another', () => {
524
+ const { browser, events } = collect()
525
+
526
+ browser.setErrorState('shaka-1001')
527
+ browser.setErrorState('shaka-3016')
528
+
529
+ expect(events).toEqual(['shaka-1001', 'shaka-3016'])
530
+ })
531
+ })