react-native-nitro-player 0.0.1 → 0.3.0-alpha.10

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 (47) hide show
  1. package/README.md +282 -2
  2. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +37 -29
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +24 -0
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +408 -16
  5. package/ios/HybridAudioRoutePicker.swift +47 -46
  6. package/ios/HybridTrackPlayer.swift +22 -0
  7. package/ios/core/TrackPlayerCore.swift +538 -48
  8. package/lib/hooks/callbackManager.d.ts +28 -0
  9. package/lib/hooks/callbackManager.js +76 -0
  10. package/lib/hooks/index.d.ts +7 -0
  11. package/lib/hooks/index.js +3 -0
  12. package/lib/hooks/useActualQueue.d.ts +48 -0
  13. package/lib/hooks/useActualQueue.js +98 -0
  14. package/lib/hooks/useNowPlaying.d.ts +36 -0
  15. package/lib/hooks/useNowPlaying.js +87 -0
  16. package/lib/hooks/useOnChangeTrack.d.ts +33 -6
  17. package/lib/hooks/useOnChangeTrack.js +65 -9
  18. package/lib/hooks/useOnPlaybackStateChange.d.ts +32 -6
  19. package/lib/hooks/useOnPlaybackStateChange.js +65 -9
  20. package/lib/hooks/usePlaylist.d.ts +48 -0
  21. package/lib/hooks/usePlaylist.js +136 -0
  22. package/lib/index.d.ts +1 -0
  23. package/lib/specs/TrackPlayer.nitro.d.ts +6 -0
  24. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +46 -9
  25. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +5 -0
  26. package/nitrogen/generated/android/c++/JRepeatMode.hpp +62 -0
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +20 -0
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/RepeatMode.kt +22 -0
  29. package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +9 -0
  30. package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Umbrella.hpp +3 -0
  31. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +44 -4
  32. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +5 -0
  33. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +64 -0
  34. package/nitrogen/generated/ios/swift/RepeatMode.swift +44 -0
  35. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +5 -0
  36. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +12 -3
  37. package/nitrogen/generated/shared/c++/RepeatMode.hpp +80 -0
  38. package/package.json +13 -12
  39. package/src/hooks/callbackManager.ts +96 -0
  40. package/src/hooks/index.ts +7 -0
  41. package/src/hooks/useActualQueue.ts +116 -0
  42. package/src/hooks/useNowPlaying.ts +97 -0
  43. package/src/hooks/useOnChangeTrack.ts +77 -13
  44. package/src/hooks/useOnPlaybackStateChange.ts +83 -13
  45. package/src/hooks/usePlaylist.ts +161 -0
  46. package/src/index.ts +1 -1
  47. package/src/specs/TrackPlayer.nitro.ts +7 -0
@@ -190,6 +190,46 @@ open class HybridTrackPlayerSpec_cxx {
190
190
  }
191
191
  }
192
192
 
193
+ @inline(__always)
194
+ public final func addToUpNext(trackId: std.string) -> bridge.Result_void_ {
195
+ do {
196
+ try self.__implementation.addToUpNext(trackId: String(trackId))
197
+ return bridge.create_Result_void_()
198
+ } catch (let __error) {
199
+ let __exceptionPtr = __error.toCpp()
200
+ return bridge.create_Result_void_(__exceptionPtr)
201
+ }
202
+ }
203
+
204
+ @inline(__always)
205
+ public final func playNext(trackId: std.string) -> bridge.Result_void_ {
206
+ do {
207
+ try self.__implementation.playNext(trackId: String(trackId))
208
+ return bridge.create_Result_void_()
209
+ } catch (let __error) {
210
+ let __exceptionPtr = __error.toCpp()
211
+ return bridge.create_Result_void_(__exceptionPtr)
212
+ }
213
+ }
214
+
215
+ @inline(__always)
216
+ public final func getActualQueue() -> bridge.Result_std__vector_TrackItem__ {
217
+ do {
218
+ let __result = try self.__implementation.getActualQueue()
219
+ let __resultCpp = { () -> bridge.std__vector_TrackItem_ in
220
+ var __vector = bridge.create_std__vector_TrackItem_(__result.count)
221
+ for __item in __result {
222
+ __vector.push_back(__item)
223
+ }
224
+ return __vector
225
+ }()
226
+ return bridge.create_Result_std__vector_TrackItem__(__resultCpp)
227
+ } catch (let __error) {
228
+ let __exceptionPtr = __error.toCpp()
229
+ return bridge.create_Result_std__vector_TrackItem__(__exceptionPtr)
230
+ }
231
+ }
232
+
193
233
  @inline(__always)
194
234
  public final func getState() -> bridge.Result_PlayerState_ {
195
235
  do {
@@ -202,6 +242,18 @@ open class HybridTrackPlayerSpec_cxx {
202
242
  }
203
243
  }
204
244
 
245
+ @inline(__always)
246
+ public final func setRepeatMode(mode: Int32) -> bridge.Result_bool_ {
247
+ do {
248
+ let __result = try self.__implementation.setRepeatMode(mode: margelo.nitro.nitroplayer.RepeatMode(rawValue: mode)!)
249
+ let __resultCpp = __result
250
+ return bridge.create_Result_bool_(__resultCpp)
251
+ } catch (let __error) {
252
+ let __exceptionPtr = __error.toCpp()
253
+ return bridge.create_Result_bool_(__exceptionPtr)
254
+ }
255
+ }
256
+
205
257
  @inline(__always)
206
258
  public final func configure(config: PlayerConfig) -> bridge.Result_void_ {
207
259
  do {
@@ -322,4 +374,16 @@ open class HybridTrackPlayerSpec_cxx {
322
374
  return bridge.create_Result_bool_(__exceptionPtr)
323
375
  }
324
376
  }
377
+
378
+ @inline(__always)
379
+ public final func setVolume(volume: Double) -> bridge.Result_bool_ {
380
+ do {
381
+ let __result = try self.__implementation.setVolume(volume: volume)
382
+ let __resultCpp = __result
383
+ return bridge.create_Result_bool_(__resultCpp)
384
+ } catch (let __error) {
385
+ let __exceptionPtr = __error.toCpp()
386
+ return bridge.create_Result_bool_(__exceptionPtr)
387
+ }
388
+ }
325
389
  }
@@ -0,0 +1,44 @@
1
+ ///
2
+ /// RepeatMode.swift
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ /**
9
+ * Represents the JS union `RepeatMode`, backed by a C++ enum.
10
+ */
11
+ public typealias RepeatMode = margelo.nitro.nitroplayer.RepeatMode
12
+
13
+ public extension RepeatMode {
14
+ /**
15
+ * Get a RepeatMode for the given String value, or
16
+ * return `nil` if the given value was invalid/unknown.
17
+ */
18
+ init?(fromString string: String) {
19
+ switch string {
20
+ case "off":
21
+ self = .off
22
+ case "Playlist":
23
+ self = .playlist
24
+ case "track":
25
+ self = .track
26
+ default:
27
+ return nil
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Get the String value this RepeatMode represents.
33
+ */
34
+ var stringValue: String {
35
+ switch self {
36
+ case .off:
37
+ return "off"
38
+ case .playlist:
39
+ return "Playlist"
40
+ case .track:
41
+ return "track"
42
+ }
43
+ }
44
+ }
@@ -20,7 +20,11 @@ namespace margelo::nitro::nitroplayer {
20
20
  prototype.registerHybridMethod("skipToNext", &HybridTrackPlayerSpec::skipToNext);
21
21
  prototype.registerHybridMethod("skipToPrevious", &HybridTrackPlayerSpec::skipToPrevious);
22
22
  prototype.registerHybridMethod("seek", &HybridTrackPlayerSpec::seek);
23
+ prototype.registerHybridMethod("addToUpNext", &HybridTrackPlayerSpec::addToUpNext);
24
+ prototype.registerHybridMethod("playNext", &HybridTrackPlayerSpec::playNext);
25
+ prototype.registerHybridMethod("getActualQueue", &HybridTrackPlayerSpec::getActualQueue);
23
26
  prototype.registerHybridMethod("getState", &HybridTrackPlayerSpec::getState);
27
+ prototype.registerHybridMethod("setRepeatMode", &HybridTrackPlayerSpec::setRepeatMode);
24
28
  prototype.registerHybridMethod("configure", &HybridTrackPlayerSpec::configure);
25
29
  prototype.registerHybridMethod("onChangeTrack", &HybridTrackPlayerSpec::onChangeTrack);
26
30
  prototype.registerHybridMethod("onPlaybackStateChange", &HybridTrackPlayerSpec::onPlaybackStateChange);
@@ -28,6 +32,7 @@ namespace margelo::nitro::nitroplayer {
28
32
  prototype.registerHybridMethod("onPlaybackProgressChange", &HybridTrackPlayerSpec::onPlaybackProgressChange);
29
33
  prototype.registerHybridMethod("onAndroidAutoConnectionChange", &HybridTrackPlayerSpec::onAndroidAutoConnectionChange);
30
34
  prototype.registerHybridMethod("isAndroidAutoConnected", &HybridTrackPlayerSpec::isAndroidAutoConnected);
35
+ prototype.registerHybridMethod("setVolume", &HybridTrackPlayerSpec::setVolume);
31
36
  });
32
37
  }
33
38
 
@@ -13,12 +13,14 @@
13
13
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
14
  #endif
15
15
 
16
+ // Forward declaration of `TrackItem` to properly resolve imports.
17
+ namespace margelo::nitro::nitroplayer { struct TrackItem; }
16
18
  // Forward declaration of `PlayerState` to properly resolve imports.
17
19
  namespace margelo::nitro::nitroplayer { struct PlayerState; }
20
+ // Forward declaration of `RepeatMode` to properly resolve imports.
21
+ namespace margelo::nitro::nitroplayer { enum class RepeatMode; }
18
22
  // Forward declaration of `PlayerConfig` to properly resolve imports.
19
23
  namespace margelo::nitro::nitroplayer { struct PlayerConfig; }
20
- // Forward declaration of `TrackItem` to properly resolve imports.
21
- namespace margelo::nitro::nitroplayer { struct TrackItem; }
22
24
  // Forward declaration of `Reason` to properly resolve imports.
23
25
  namespace margelo::nitro::nitroplayer { enum class Reason; }
24
26
  // Forward declaration of `TrackPlayerState` to properly resolve imports.
@@ -26,9 +28,11 @@ namespace margelo::nitro::nitroplayer { enum class TrackPlayerState; }
26
28
 
27
29
  #include <string>
28
30
  #include <optional>
31
+ #include "TrackItem.hpp"
32
+ #include <vector>
29
33
  #include "PlayerState.hpp"
34
+ #include "RepeatMode.hpp"
30
35
  #include "PlayerConfig.hpp"
31
- #include "TrackItem.hpp"
32
36
  #include "Reason.hpp"
33
37
  #include <functional>
34
38
  #include "TrackPlayerState.hpp"
@@ -70,7 +74,11 @@ namespace margelo::nitro::nitroplayer {
70
74
  virtual void skipToNext() = 0;
71
75
  virtual void skipToPrevious() = 0;
72
76
  virtual void seek(double position) = 0;
77
+ virtual void addToUpNext(const std::string& trackId) = 0;
78
+ virtual void playNext(const std::string& trackId) = 0;
79
+ virtual std::vector<TrackItem> getActualQueue() = 0;
73
80
  virtual PlayerState getState() = 0;
81
+ virtual bool setRepeatMode(RepeatMode mode) = 0;
74
82
  virtual void configure(const PlayerConfig& config) = 0;
75
83
  virtual void onChangeTrack(const std::function<void(const TrackItem& /* track */, std::optional<Reason> /* reason */)>& callback) = 0;
76
84
  virtual void onPlaybackStateChange(const std::function<void(TrackPlayerState /* state */, std::optional<Reason> /* reason */)>& callback) = 0;
@@ -78,6 +86,7 @@ namespace margelo::nitro::nitroplayer {
78
86
  virtual void onPlaybackProgressChange(const std::function<void(double /* position */, double /* totalDuration */, std::optional<bool> /* isManuallySeeked */)>& callback) = 0;
79
87
  virtual void onAndroidAutoConnectionChange(const std::function<void(bool /* connected */)>& callback) = 0;
80
88
  virtual bool isAndroidAutoConnected() = 0;
89
+ virtual bool setVolume(double volume) = 0;
81
90
 
82
91
  protected:
83
92
  // Hybrid Setup
@@ -0,0 +1,80 @@
1
+ ///
2
+ /// RepeatMode.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 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::nitroplayer {
27
+
28
+ /**
29
+ * An enum which can be represented as a JavaScript union (RepeatMode).
30
+ */
31
+ enum class RepeatMode {
32
+ OFF SWIFT_NAME(off) = 0,
33
+ PLAYLIST SWIFT_NAME(playlist) = 1,
34
+ TRACK SWIFT_NAME(track) = 2,
35
+ } CLOSED_ENUM;
36
+
37
+ } // namespace margelo::nitro::nitroplayer
38
+
39
+ namespace margelo::nitro {
40
+
41
+ // C++ RepeatMode <> JS RepeatMode (union)
42
+ template <>
43
+ struct JSIConverter<margelo::nitro::nitroplayer::RepeatMode> final {
44
+ static inline margelo::nitro::nitroplayer::RepeatMode fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
45
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
46
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
47
+ case hashString("off"): return margelo::nitro::nitroplayer::RepeatMode::OFF;
48
+ case hashString("Playlist"): return margelo::nitro::nitroplayer::RepeatMode::PLAYLIST;
49
+ case hashString("track"): return margelo::nitro::nitroplayer::RepeatMode::TRACK;
50
+ default: [[unlikely]]
51
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum RepeatMode - invalid value!");
52
+ }
53
+ }
54
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitroplayer::RepeatMode arg) {
55
+ switch (arg) {
56
+ case margelo::nitro::nitroplayer::RepeatMode::OFF: return JSIConverter<std::string>::toJSI(runtime, "off");
57
+ case margelo::nitro::nitroplayer::RepeatMode::PLAYLIST: return JSIConverter<std::string>::toJSI(runtime, "Playlist");
58
+ case margelo::nitro::nitroplayer::RepeatMode::TRACK: return JSIConverter<std::string>::toJSI(runtime, "track");
59
+ default: [[unlikely]]
60
+ throw std::invalid_argument("Cannot convert RepeatMode to JS - invalid value: "
61
+ + std::to_string(static_cast<int>(arg)) + "!");
62
+ }
63
+ }
64
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
65
+ if (!value.isString()) {
66
+ return false;
67
+ }
68
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
69
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
70
+ case hashString("off"):
71
+ case hashString("Playlist"):
72
+ case hashString("track"):
73
+ return true;
74
+ default:
75
+ return false;
76
+ }
77
+ }
78
+ };
79
+
80
+ } // namespace margelo::nitro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-player",
3
- "version": "0.0.1",
3
+ "version": "0.3.0-alpha.10",
4
4
  "description": "react-native-nitro-player",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",
@@ -30,12 +30,14 @@
30
30
  "scripts": {
31
31
  "postinstall": "tsc || exit 0;",
32
32
  "typecheck": "tsc --noEmit",
33
- "clean": "rm -rf android/build node_modules/**/android/build lib",
33
+ "clean": "rm -rf android/build node_modules/**/android/build lib nitrogen tsconfig.tsbuildinfo",
34
34
  "lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
35
35
  "lint-ci": "eslint \"**/*.{js,ts,tsx}\" -f @jamesacarr/github-actions",
36
36
  "typescript": "tsc",
37
- "specs": "tsc --noEmit false && nitrogen --logLevel=\"debug\"",
38
- "release": "release-it"
37
+ "specs": "tsc && nitrogen --logLevel=\"debug\"",
38
+ "copy-readme": "cp ../README.md README.md",
39
+ "release": "release-it",
40
+ "build": "bun run clean && bun run specs && bun run typescript && bun run copy-readme"
39
41
  },
40
42
  "keywords": [
41
43
  "react-native",
@@ -43,14 +45,14 @@
43
45
  ],
44
46
  "repository": {
45
47
  "type": "git",
46
- "url": "git+https://github.com/mrousavy/nitro.git"
48
+ "url": "git+https://github.com/riteshshukla04/react-native-nitro-player"
47
49
  },
48
- "author": "Marc Rousavy <me@mrousavy.com> (https://github.com/mrousavy)",
50
+ "author": "Ritesh Shukla <riteshshukla2381@gmail.com> (https://github.com/riteshshukla04)",
49
51
  "license": "MIT",
50
52
  "bugs": {
51
- "url": "https://github.com/mrousavy/nitro/issues"
53
+ "url": "https://github.com/riteshshukla04/react-native-nitro-player/issues"
52
54
  },
53
- "homepage": "https://github.com/mrousavy/nitro#readme",
55
+ "homepage": "https://github.com/riteshshukla04/react-native-nitro-player#readme",
54
56
  "publishConfig": {
55
57
  "registry": "https://registry.npmjs.org/"
56
58
  },
@@ -122,14 +124,13 @@
122
124
  "github": {
123
125
  "release": true
124
126
  },
127
+ "hooks": {
128
+ "before:release": "bun run clean && bun run specs && bun run typescript && bun run copy-readme"
129
+ },
125
130
  "plugins": {
126
131
  "@release-it/bumper": {
127
132
  "in": "package.json",
128
133
  "out": [
129
- {
130
- "file": "package.json",
131
- "path": "version"
132
- },
133
134
  {
134
135
  "file": "../package.json",
135
136
  "path": "version"
@@ -0,0 +1,96 @@
1
+ import { TrackPlayer } from '../index'
2
+ import type { TrackItem, TrackPlayerState, Reason } from '../types/PlayerQueue'
3
+
4
+ type PlaybackStateCallback = (state: TrackPlayerState, reason?: Reason) => void
5
+ type TrackChangeCallback = (track: TrackItem, reason?: Reason) => void
6
+
7
+ /**
8
+ * Internal subscription manager that allows multiple hooks to subscribe
9
+ * to a single native callback. This solves the problem where registering
10
+ * a new callback overwrites the previous one.
11
+ */
12
+ class CallbackSubscriptionManager {
13
+ private playbackStateSubscribers = new Set<PlaybackStateCallback>()
14
+ private trackChangeSubscribers = new Set<TrackChangeCallback>()
15
+ private isPlaybackStateRegistered = false
16
+ private isTrackChangeRegistered = false
17
+
18
+ /**
19
+ * Subscribe to playback state changes
20
+ * @returns Unsubscribe function
21
+ */
22
+ subscribeToPlaybackState(callback: PlaybackStateCallback): () => void {
23
+ this.playbackStateSubscribers.add(callback)
24
+ this.ensurePlaybackStateRegistered()
25
+
26
+ return () => {
27
+ this.playbackStateSubscribers.delete(callback)
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Subscribe to track changes
33
+ * @returns Unsubscribe function
34
+ */
35
+ subscribeToTrackChange(callback: TrackChangeCallback): () => void {
36
+ this.trackChangeSubscribers.add(callback)
37
+ this.ensureTrackChangeRegistered()
38
+
39
+ return () => {
40
+ this.trackChangeSubscribers.delete(callback)
41
+ }
42
+ }
43
+
44
+ private ensurePlaybackStateRegistered(): void {
45
+ if (this.isPlaybackStateRegistered) return
46
+
47
+ try {
48
+ TrackPlayer.onPlaybackStateChange((state, reason) => {
49
+ this.playbackStateSubscribers.forEach((subscriber) => {
50
+ try {
51
+ subscriber(state, reason)
52
+ } catch (error) {
53
+ console.error(
54
+ '[CallbackManager] Error in playback state subscriber:',
55
+ error
56
+ )
57
+ }
58
+ })
59
+ })
60
+ this.isPlaybackStateRegistered = true
61
+ } catch (error) {
62
+ console.error(
63
+ '[CallbackManager] Failed to register playback state callback:',
64
+ error
65
+ )
66
+ }
67
+ }
68
+
69
+ private ensureTrackChangeRegistered(): void {
70
+ if (this.isTrackChangeRegistered) return
71
+
72
+ try {
73
+ TrackPlayer.onChangeTrack((track, reason) => {
74
+ this.trackChangeSubscribers.forEach((subscriber) => {
75
+ try {
76
+ subscriber(track, reason)
77
+ } catch (error) {
78
+ console.error(
79
+ '[CallbackManager] Error in track change subscriber:',
80
+ error
81
+ )
82
+ }
83
+ })
84
+ })
85
+ this.isTrackChangeRegistered = true
86
+ } catch (error) {
87
+ console.error(
88
+ '[CallbackManager] Failed to register track change callback:',
89
+ error
90
+ )
91
+ }
92
+ }
93
+ }
94
+
95
+ // Export singleton instance
96
+ export const callbackManager = new CallbackSubscriptionManager()
@@ -1,6 +1,13 @@
1
1
  export { useOnChangeTrack } from './useOnChangeTrack'
2
+ export type { TrackChangeResult } from './useOnChangeTrack'
2
3
  export { useOnPlaybackStateChange } from './useOnPlaybackStateChange'
4
+ export type { PlaybackStateResult } from './useOnPlaybackStateChange'
3
5
  export { useOnSeek } from './useOnSeek'
4
6
  export { useOnPlaybackProgressChange } from './useOnPlaybackProgressChange'
5
7
  export { useAndroidAutoConnection } from './useAndroidAutoConnection'
6
8
  export { useAudioDevices } from './useAudioDevices'
9
+ export { useNowPlaying } from './useNowPlaying'
10
+ export { usePlaylist } from './usePlaylist'
11
+ export type { UsePlaylistResult } from './usePlaylist'
12
+ export { useActualQueue } from './useActualQueue'
13
+ export type { UseActualQueueResult } from './useActualQueue'
@@ -0,0 +1,116 @@
1
+ import { useEffect, useState, useRef, useCallback } from 'react'
2
+ import { TrackPlayer } from '../index'
3
+ import { callbackManager } from './callbackManager'
4
+ import type { TrackItem } from '../types/PlayerQueue'
5
+
6
+ export interface UseActualQueueResult {
7
+ /** The current queue in playback order */
8
+ queue: TrackItem[]
9
+ /** Manually refresh the queue */
10
+ refreshQueue: () => void
11
+ /** Whether the queue is currently loading */
12
+ isLoading: boolean
13
+ }
14
+
15
+ /**
16
+ * Hook to get the actual playback queue including temporary tracks
17
+ *
18
+ * Returns the complete queue in playback order:
19
+ * [tracks_before_current] + [current] + [playNext_stack] + [upNext_queue] + [remaining_tracks]
20
+ *
21
+ * Auto-updates when:
22
+ * - Track changes
23
+ * - Playback state changes
24
+ *
25
+ * Call `refreshQueue()` after adding tracks via `playNext()` or `addToUpNext()`
26
+ * to immediately see the updated queue.
27
+ *
28
+ * @returns Object containing queue array, refresh function, and loading state
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * function QueueView() {
33
+ * const { queue, refreshQueue, isLoading } = useActualQueue();
34
+ *
35
+ * const handleAddToUpNext = (trackId: string) => {
36
+ * TrackPlayer.addToUpNext(trackId);
37
+ * // Refresh queue after adding track
38
+ * setTimeout(refreshQueue, 100);
39
+ * };
40
+ *
41
+ * return (
42
+ * <ScrollView>
43
+ * {queue.map((track, index) => (
44
+ * <Text key={track.id}>
45
+ * {index + 1}. {track.title}
46
+ * </Text>
47
+ * ))}
48
+ * </ScrollView>
49
+ * );
50
+ * }
51
+ * ```
52
+ */
53
+ export function useActualQueue(): UseActualQueueResult {
54
+ const [queue, setQueue] = useState<TrackItem[]>([])
55
+ const [isLoading, setIsLoading] = useState(true)
56
+ const isMounted = useRef(true)
57
+
58
+ const updateQueue = useCallback(() => {
59
+ if (!isMounted.current) return
60
+
61
+ try {
62
+ const actualQueue = TrackPlayer.getActualQueue()
63
+ if (isMounted.current) {
64
+ setQueue(actualQueue)
65
+ setIsLoading(false)
66
+ }
67
+ } catch (error) {
68
+ console.error('[useActualQueue] Error getting queue:', error)
69
+ if (isMounted.current) {
70
+ setQueue([])
71
+ setIsLoading(false)
72
+ }
73
+ }
74
+ }, [])
75
+
76
+ const refreshQueue = useCallback(() => {
77
+ if (!isMounted.current) return
78
+ setIsLoading(true)
79
+ updateQueue()
80
+ }, [updateQueue])
81
+
82
+ // Initialize queue
83
+ useEffect(() => {
84
+ isMounted.current = true
85
+ updateQueue()
86
+
87
+ return () => {
88
+ isMounted.current = false
89
+ }
90
+ }, [updateQueue])
91
+
92
+ // Update queue on track changes (with slight delay to ensure native side has updated)
93
+ useEffect(() => {
94
+ const unsubscribe = callbackManager.subscribeToTrackChange(() => {
95
+ // Small delay to ensure native queue is updated
96
+ setTimeout(updateQueue, 50)
97
+ })
98
+
99
+ return () => {
100
+ unsubscribe()
101
+ }
102
+ }, [updateQueue])
103
+
104
+ // Update queue on playback state changes
105
+ useEffect(() => {
106
+ const unsubscribe = callbackManager.subscribeToPlaybackState(() => {
107
+ updateQueue()
108
+ })
109
+
110
+ return () => {
111
+ unsubscribe()
112
+ }
113
+ }, [updateQueue])
114
+
115
+ return { queue, refreshQueue, isLoading }
116
+ }
@@ -0,0 +1,97 @@
1
+ import { useEffect, useState, useRef, useCallback } from 'react'
2
+ import { TrackPlayer } from '../index'
3
+ import { callbackManager } from './callbackManager'
4
+ import type { PlayerState } from '../types/PlayerQueue'
5
+
6
+ const DEFAULT_STATE: PlayerState = {
7
+ currentTrack: null,
8
+ currentPosition: 0,
9
+ totalDuration: 0,
10
+ currentState: 'stopped',
11
+ currentPlaylistId: null,
12
+ currentIndex: -1,
13
+ }
14
+
15
+ /**
16
+ * Hook to get the current player state (same as TrackPlayer.getState())
17
+ *
18
+ * This hook provides all player state information including:
19
+ * - Current track
20
+ * - Current position and duration
21
+ * - Playback state (playing, paused, stopped)
22
+ * - Current playlist ID
23
+ * - Current track index
24
+ *
25
+ * The hook uses native callbacks for immediate updates when state changes.
26
+ * Multiple components can use this hook simultaneously.
27
+ *
28
+ * @returns PlayerState object with all current player information
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * function PlayerComponent() {
33
+ * const state = useNowPlaying()
34
+ *
35
+ * return (
36
+ * <View>
37
+ * {state.currentTrack && (
38
+ * <Text>Now Playing: {state.currentTrack.title}</Text>
39
+ * )}
40
+ * <Text>Position: {state.currentPosition} / {state.totalDuration}</Text>
41
+ * <Text>State: {state.currentState}</Text>
42
+ * <Text>Playlist: {state.currentPlaylistId || 'None'}</Text>
43
+ * <Text>Index: {state.currentIndex}</Text>
44
+ * </View>
45
+ * )
46
+ * }
47
+ * ```
48
+ */
49
+ export function useNowPlaying(): PlayerState {
50
+ const [state, setState] = useState<PlayerState>(DEFAULT_STATE)
51
+ const isMounted = useRef(true)
52
+
53
+ const updateState = useCallback(() => {
54
+ if (!isMounted.current) return
55
+
56
+ try {
57
+ const newState = TrackPlayer.getState()
58
+ setState(newState)
59
+ } catch (error) {
60
+ console.error('[useNowPlaying] Error updating player state:', error)
61
+ }
62
+ }, [])
63
+
64
+ // Initialize with current state
65
+ useEffect(() => {
66
+ isMounted.current = true
67
+ updateState()
68
+
69
+ return () => {
70
+ isMounted.current = false
71
+ }
72
+ }, [updateState])
73
+
74
+ // Subscribe to track changes
75
+ useEffect(() => {
76
+ const unsubscribe = callbackManager.subscribeToTrackChange(() => {
77
+ updateState()
78
+ })
79
+
80
+ return () => {
81
+ unsubscribe()
82
+ }
83
+ }, [updateState])
84
+
85
+ // Subscribe to playback state changes
86
+ useEffect(() => {
87
+ const unsubscribe = callbackManager.subscribeToPlaybackState(() => {
88
+ updateState()
89
+ })
90
+
91
+ return () => {
92
+ unsubscribe()
93
+ }
94
+ }, [updateState])
95
+
96
+ return state
97
+ }