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.
- package/android/build.gradle +2 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +160 -106
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +11 -29
- package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +377 -110
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +13 -1
- package/android/src/main/res/values/strings.xml +6 -21
- package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
- package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
- package/lib/commonjs/features/browser.js +8 -0
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/web/NativeAudioBrowser.js +163 -50
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/Player.js +232 -39
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +30 -12
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js +16 -19
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
- package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
- package/lib/module/features/browser.js +8 -0
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/web/NativeAudioBrowser.js +163 -50
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
- package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/module/web/TrackPlayer/Player.js +232 -39
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +30 -12
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js +16 -19
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/http/RequestConfigBuilder.js +110 -27
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/http/followMediaRedirect.js +215 -0
- package/lib/module/web/http/followMediaRedirect.js.map +1 -0
- package/lib/typescript/src/features/browser.d.ts +8 -0
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +13 -4
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/features/browser.ts +8 -0
- package/src/types/browser.ts +13 -4
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
- package/src/web/NativeAudioBrowser.ts +193 -60
- package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
- package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
- package/src/web/TrackPlayer/Player.load.test.ts +137 -1
- package/src/web/TrackPlayer/Player.ts +256 -37
- package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
- package/src/web/__tests__/lifecycle.test.ts +345 -0
- package/src/web/__tests__/lifecycleHarness.ts +351 -0
- package/src/web/__tests__/loadEdgeCases.test.ts +195 -0
- package/src/web/__tests__/loadSupersession.test.ts +175 -0
- package/src/web/__tests__/retry.test.ts +169 -0
- package/src/web/browser/BrowserManager.ts +32 -15
- package/src/web/browser/SearchManager.test.ts +75 -0
- package/src/web/browser/SearchManager.ts +23 -26
- package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
- package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
- package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
- package/src/web/http/RequestConfigBuilder.test.ts +241 -1
- package/src/web/http/RequestConfigBuilder.ts +140 -33
- package/src/web/http/followMediaRedirect.test.ts +289 -0
- 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
|
|
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.
|
|
376
|
-
*
|
|
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
|
-
*
|
|
422
|
-
* `
|
|
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
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../../src/types/browser.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/features/browser.ts
CHANGED
|
@@ -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
|
*
|
package/src/types/browser.ts
CHANGED
|
@@ -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.
|
|
436
|
-
*
|
|
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
|
-
*
|
|
482
|
-
* `
|
|
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
|
|
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
|
+
})
|