react-native-queue-player 1.0.8 → 1.1.0

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 (59) hide show
  1. package/README.md +12 -6
  2. package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +21 -3
  3. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaceholderArtwork.kt +30 -0
  4. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaceholderFallbackBitmapLoader.kt +9 -6
  5. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +4 -1
  6. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +57 -10
  7. package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +53 -20
  8. package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +55 -0
  9. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaceholderArtworkTest.kt +72 -0
  10. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaceholderFallbackBitmapLoaderTest.kt +21 -9
  11. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceArtworkLoaderTest.kt +9 -9
  12. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +1 -1
  13. package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +103 -0
  14. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +1 -1
  15. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +1 -1
  16. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +3 -3
  17. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +7 -7
  18. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +6 -6
  19. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +2 -2
  20. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +1 -1
  21. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueChangeTest.kt +1 -1
  22. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +1 -1
  23. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +1 -1
  24. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +41 -2
  25. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +34 -1
  26. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +1 -1
  27. package/ios/Cast/Core/CastNowPlayingController.swift +7 -4
  28. package/ios/NowPlayingInfo.swift +7 -9
  29. package/ios/PlaceholderArtwork.swift +24 -1
  30. package/ios/QueueSkipArithmetic.swift +55 -9
  31. package/ios/Tests/AVQueueBuilderTests.swift +9 -9
  32. package/ios/Tests/CastNowPlayingControllerTests.swift +33 -0
  33. package/ios/Tests/PlaceholderArtworkTests.swift +81 -0
  34. package/ios/Tests/SkipIndexTests.swift +97 -0
  35. package/ios/TrackPlayer.swift +42 -14
  36. package/lib/module/index.js.map +1 -1
  37. package/lib/module/types.js +9 -0
  38. package/lib/module/types.js.map +1 -1
  39. package/lib/typescript/TrackPlayer.nitro.d.ts +7 -7
  40. package/lib/typescript/index.d.ts +1 -1
  41. package/lib/typescript/index.d.ts.map +1 -1
  42. package/lib/typescript/types.d.ts +41 -0
  43. package/lib/typescript/types.d.ts.map +1 -1
  44. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +4 -0
  45. package/nitrogen/generated/android/c++/JPlayerConfig.hpp +11 -1
  46. package/nitrogen/generated/android/c++/JSkipToPreviousBehavior.hpp +58 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +12 -2
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SkipToPreviousBehavior.kt +23 -0
  49. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +18 -0
  50. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +3 -0
  51. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +3 -0
  52. package/nitrogen/generated/ios/swift/PlayerConfig.swift +30 -1
  53. package/nitrogen/generated/ios/swift/SkipToPreviousBehavior.swift +40 -0
  54. package/nitrogen/generated/shared/c++/PlayerConfig.hpp +12 -1
  55. package/nitrogen/generated/shared/c++/SkipToPreviousBehavior.hpp +76 -0
  56. package/package.json +1 -1
  57. package/src/TrackPlayer.nitro.ts +7 -7
  58. package/src/index.ts +1 -0
  59. package/src/types.ts +42 -0
@@ -0,0 +1,76 @@
1
+ ///
2
+ /// SkipToPreviousBehavior.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/NitroHash.hpp>)
11
+ #include <NitroModules/NitroHash.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
16
+ #include <NitroModules/JSIConverter.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
21
+ #include <NitroModules/NitroDefines.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+
26
+ namespace margelo::nitro::queueplayer {
27
+
28
+ /**
29
+ * An enum which can be represented as a JavaScript union (SkipToPreviousBehavior).
30
+ */
31
+ enum class SkipToPreviousBehavior {
32
+ PREVIOUS SWIFT_NAME(previous) = 0,
33
+ RESTART_OR_PREVIOUS SWIFT_NAME(restartOrPrevious) = 1,
34
+ } CLOSED_ENUM;
35
+
36
+ } // namespace margelo::nitro::queueplayer
37
+
38
+ namespace margelo::nitro {
39
+
40
+ // C++ SkipToPreviousBehavior <> JS SkipToPreviousBehavior (union)
41
+ template <>
42
+ struct JSIConverter<margelo::nitro::queueplayer::SkipToPreviousBehavior> final {
43
+ static inline margelo::nitro::queueplayer::SkipToPreviousBehavior fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
44
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
45
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
46
+ case hashString("previous"): return margelo::nitro::queueplayer::SkipToPreviousBehavior::PREVIOUS;
47
+ case hashString("restart-or-previous"): return margelo::nitro::queueplayer::SkipToPreviousBehavior::RESTART_OR_PREVIOUS;
48
+ default: [[unlikely]]
49
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum SkipToPreviousBehavior - invalid value!");
50
+ }
51
+ }
52
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::queueplayer::SkipToPreviousBehavior arg) {
53
+ switch (arg) {
54
+ case margelo::nitro::queueplayer::SkipToPreviousBehavior::PREVIOUS: return JSIConverter<std::string>::toJSI(runtime, "previous");
55
+ case margelo::nitro::queueplayer::SkipToPreviousBehavior::RESTART_OR_PREVIOUS: return JSIConverter<std::string>::toJSI(runtime, "restart-or-previous");
56
+ default: [[unlikely]]
57
+ throw std::invalid_argument("Cannot convert SkipToPreviousBehavior to JS - invalid value: "
58
+ + std::to_string(static_cast<int>(arg)) + "!");
59
+ }
60
+ }
61
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
62
+ if (!value.isString()) {
63
+ return false;
64
+ }
65
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
66
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
67
+ case hashString("previous"):
68
+ case hashString("restart-or-previous"):
69
+ return true;
70
+ default:
71
+ return false;
72
+ }
73
+ }
74
+ };
75
+
76
+ } // namespace margelo::nitro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-queue-player",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Nitro Modules audio playback library for React Native — gapless, EQ, crossfade, automotive, voice, casting",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/index.d.ts",
@@ -306,17 +306,17 @@ export interface TrackPlayer
306
306
  getCanSkipNext(): boolean;
307
307
  /**
308
308
  * Whether the user-facing Previous button should be enabled.
309
- * Symmetric with `getCanSkipNext` — true when there's a previous
309
+ * Symmetric with `getCanSkipNext` — with the default
310
+ * `skipToPreviousBehavior: 'previous'`, true when there's a previous
310
311
  * track to skip to under the current repeat mode:
311
312
  * - `'off'` and `'track'` at index 0: false (no previous, no wrap)
312
313
  * - `'queue'`: true with an active track (wraps)
313
314
  *
314
- * **Apps that want the Apple-Music-style "tap Previous restarts
315
- * current when position > 3s" UX** should implement that
316
- * themselves in their button handler branch on the live
317
- * `useProgress().position` value to either `seekTo(0)` or
318
- * `skipToPrevious()`. The lib intentionally ships the primitive,
319
- * not the policy.
315
+ * With `PlayerConfig.skipToPreviousBehavior: 'restart-or-previous'` the
316
+ * lib applies the standard music-player "restart when past ~3 s, else
317
+ * previous" model natively, and this reports `true` whenever a track is
318
+ * loaded (even at index 0, where past the threshold Previous restarts the
319
+ * current track) so the control never greys out.
320
320
  */
321
321
  getCanSkipPrevious(): boolean;
322
322
  /**
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export type {
14
14
  PlaybackMode,
15
15
  PlaybackModeKind,
16
16
  RepeatMode,
17
+ SkipToPreviousBehavior,
17
18
  RemoteSkipMode,
18
19
  RemoteControlOptions,
19
20
  StateChangeReason,
package/src/types.ts CHANGED
@@ -69,6 +69,23 @@ export interface PlayerConfig {
69
69
  httpHeaders?: Record<string, string>;
70
70
  /** Overrides the default UA on network requests. */
71
71
  userAgent?: string;
72
+ /**
73
+ * Replacement for the built-in RNQP cover-art placeholder (the image
74
+ * shown before real artwork loads and when a track has no artwork).
75
+ * MUST be a **local `file://` URI** — the built-in placeholder is a
76
+ * local file, so this must be too; remote URLs are not loaded (they
77
+ * would break offline playback and change behaviour). When set, this
78
+ * image is used in place of the built-in everywhere the built-in is
79
+ * used; unset, non-`file://`, or unloadable falls back to the built-in.
80
+ * Resolve a bundled asset to a `file://` path with expo-asset —
81
+ * `const a = Asset.fromModule(require('./logo.png')); await
82
+ * a.downloadAsync();` then pass `a.localUri` — which copies the packaged
83
+ * image onto the filesystem across dev and release.
84
+ * (`Image.resolveAssetSource(...).uri` is not suitable: it is an
85
+ * `http://` Metro URL in dev and a drawable resource name on Android
86
+ * release, neither of which is a `file://` path.)
87
+ */
88
+ placeholderArtworkUri?: string;
72
89
  /**
73
90
  * Auto-retry attempts on transient errors (TIMEOUT / CONNECTION_LOST /
74
91
  * NETWORK_UNREACHABLE / SOURCE_UNREACHABLE / CANNOT_OPEN). Clamped
@@ -155,6 +172,21 @@ export interface PlayerConfig {
155
172
  * Default: `false` (targets pass through unclamped).
156
173
  */
157
174
  clampSeekToBuffered?: boolean;
175
+ /**
176
+ * How skip-to-previous behaves. `'previous'` (default) always goes to the
177
+ * previous track. `'restart-or-previous'` restarts the current track
178
+ * (seeks to 0) when it is past ~3 s, otherwise goes to the previous track
179
+ * — the standard music-player model. Applies
180
+ * to local playback; during cast, Previous routes to the receiver. When
181
+ * `'restart-or-previous'`, the Previous control is always enabled
182
+ * (`canSkipPrevious` reports `true` whenever a track is loaded, so the
183
+ * control never greys out — even on the first track, where past the
184
+ * threshold it restarts).
185
+ *
186
+ * Pinned per `configure()` (a fixed behavior, not a live toggle);
187
+ * changing it re-runs `configure()`. Default: `'previous'`.
188
+ */
189
+ skipToPreviousBehavior?: SkipToPreviousBehavior;
158
190
  /**
159
191
  * Disk budget for the lookahead cache, in megabytes. Fixed at `configure()`
160
192
  * time and cannot change at runtime — Android's Media3 `SimpleCache` fixes
@@ -322,6 +354,16 @@ export const crossfadeDurationBounds = {
322
354
  */
323
355
  export type RepeatMode = 'off' | 'queue' | 'track';
324
356
 
357
+ /**
358
+ * How `skipToPrevious` (and the OS Previous control) behaves.
359
+ * - `'previous'` — always go to the previous track (the default; current
360
+ * behavior).
361
+ * - `'restart-or-previous'` — restart the current track (seek to 0) when it
362
+ * is past ~3 s, otherwise go to the previous track. The standard
363
+ * music-player model. See `PlayerConfig.skipToPreviousBehavior`.
364
+ */
365
+ export type SkipToPreviousBehavior = 'previous' | 'restart-or-previous';
366
+
325
367
  /**
326
368
  * How the lock-screen / notification skip controls behave: skip between queue
327
369
  * tracks (`'track'`) or jump forward/back within the current track by a fixed