@capgo/native-audio 8.2.14 → 8.3.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.
package/android/build.gradle
CHANGED
|
@@ -79,7 +79,7 @@ dependencies {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
implementation 'androidx.media3:media3-session:1.9.2'
|
|
82
|
-
implementation 'androidx.media3:media3-transformer:1.9.
|
|
82
|
+
implementation 'androidx.media3:media3-transformer:1.9.2'
|
|
83
83
|
implementation 'androidx.media3:media3-ui:1.9.0'
|
|
84
84
|
implementation 'androidx.media3:media3-database:1.9.2'
|
|
85
85
|
implementation 'androidx.media3:media3-common:1.9.2'
|
|
@@ -12,7 +12,7 @@ enum MyError: Error {
|
|
|
12
12
|
@objc(NativeAudio)
|
|
13
13
|
// swiftlint:disable:next type_body_length
|
|
14
14
|
public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
15
|
-
private let pluginVersion: String = "8.
|
|
15
|
+
private let pluginVersion: String = "8.3.0"
|
|
16
16
|
public let identifier = "NativeAudio"
|
|
17
17
|
public let jsName = "NativeAudio"
|
|
18
18
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -49,6 +49,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
private let queueKey = DispatchSpecificKey<Bool>()
|
|
52
|
+
/// Set while executing a block on the audio queue so getAudioAsset/endSession can avoid reentrant sync (deadlock).
|
|
53
|
+
private let audioQueueContextKey = DispatchSpecificKey<Bool?>()
|
|
52
54
|
var session = AVAudioSession.sharedInstance()
|
|
53
55
|
|
|
54
56
|
// Track if audio session has been initialized
|
|
@@ -187,7 +189,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
187
189
|
|
|
188
190
|
if !asset.isPlaying() {
|
|
189
191
|
asset.resume()
|
|
190
|
-
self.
|
|
192
|
+
self.updateNowPlayingInfo(audioId: assetId, audioAsset: asset)
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
return .success
|
|
@@ -222,9 +224,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
asset.stop()
|
|
225
|
-
self.clearNowPlayingInfo()
|
|
226
227
|
self.currentlyPlayingAssetId = nil
|
|
227
|
-
self.updatePlaybackState(isPlaying: false)
|
|
228
228
|
}
|
|
229
229
|
return .success
|
|
230
230
|
}
|
|
@@ -245,7 +245,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
245
245
|
self.updatePlaybackState(isPlaying: false)
|
|
246
246
|
} else {
|
|
247
247
|
asset.resume()
|
|
248
|
-
self.
|
|
248
|
+
self.updateNowPlayingInfo(audioId: assetId, audioAsset: asset)
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
return .success
|
|
@@ -273,7 +273,11 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
273
273
|
let focus = call.getBool(Constant.FocusAudio) ?? false
|
|
274
274
|
let background = call.getBool(Constant.Background) ?? false
|
|
275
275
|
let ignoreSilent = call.getBool(Constant.IgnoreSilent) ?? true
|
|
276
|
-
|
|
276
|
+
// Only update showNotification when explicitly provided so repeated configure() calls
|
|
277
|
+
// (e.g. when switching assets) don't reset it to false and break Now Playing for the next play
|
|
278
|
+
if let showNotification = call.getBool(Constant.ShowNotification) {
|
|
279
|
+
self.showNotification = showNotification
|
|
280
|
+
}
|
|
277
281
|
|
|
278
282
|
logger.info("Configuring audio session with focus=%@ background=%@ ignoreSilent=%@", "\(focus)", "\(background)", "\(ignoreSilent)")
|
|
279
283
|
|
|
@@ -415,9 +419,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
415
419
|
self.playOnceAssets.remove(assetId)
|
|
416
420
|
self.notificationMetadataMap.removeValue(forKey: assetId)
|
|
417
421
|
|
|
418
|
-
//
|
|
422
|
+
// Reset current track if this was the currently playing asset (next play will overwrite Now Playing)
|
|
419
423
|
if self.currentlyPlayingAssetId == assetId {
|
|
420
|
-
self.clearNowPlayingInfo()
|
|
421
424
|
self.currentlyPlayingAssetId = nil
|
|
422
425
|
}
|
|
423
426
|
|
|
@@ -605,14 +608,24 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
605
608
|
|
|
606
609
|
func endSession() {
|
|
607
610
|
do {
|
|
608
|
-
//
|
|
609
|
-
let hasPlayingAssets
|
|
610
|
-
|
|
611
|
+
// Avoid reentrant sync when already on audio queue (e.g. from pause()) to prevent deadlock
|
|
612
|
+
let hasPlayingAssets: Bool
|
|
613
|
+
if DispatchQueue.getSpecific(key: audioQueueContextKey) == true {
|
|
614
|
+
hasPlayingAssets = self.audioList.values.contains { asset in
|
|
611
615
|
if let audioAsset = asset as? AudioAsset {
|
|
612
616
|
return audioAsset.isPlaying()
|
|
613
617
|
}
|
|
614
618
|
return false
|
|
615
619
|
}
|
|
620
|
+
} else {
|
|
621
|
+
hasPlayingAssets = audioQueue.sync {
|
|
622
|
+
return self.audioList.values.contains { asset in
|
|
623
|
+
if let audioAsset = asset as? AudioAsset {
|
|
624
|
+
return audioAsset.isPlaying()
|
|
625
|
+
}
|
|
626
|
+
return false
|
|
627
|
+
}
|
|
628
|
+
}
|
|
616
629
|
}
|
|
617
630
|
|
|
618
631
|
// Only deactivate if no assets are playing AND no other audio is active
|
|
@@ -727,16 +740,21 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
727
740
|
}
|
|
728
741
|
|
|
729
742
|
@objc private func getAudioAsset(_ call: CAPPluginCall) -> AudioAsset? {
|
|
743
|
+
// Avoid reentrant sync when already on audio queue (e.g. from pause()) to prevent deadlock
|
|
744
|
+
if DispatchQueue.getSpecific(key: audioQueueContextKey) == true {
|
|
745
|
+
return self.audioList[call.getString(Constant.AssetIdKey) ?? ""] as? AudioAsset
|
|
746
|
+
}
|
|
730
747
|
var asset: AudioAsset?
|
|
731
|
-
audioQueue.sync {
|
|
748
|
+
audioQueue.sync {
|
|
732
749
|
asset = self.audioList[call.getString(Constant.AssetIdKey) ?? ""] as? AudioAsset
|
|
733
750
|
}
|
|
734
751
|
return asset
|
|
735
752
|
}
|
|
736
753
|
|
|
737
754
|
@objc func setCurrentTime(_ call: CAPPluginCall) {
|
|
738
|
-
// Consistent use of audioQueue.sync for all operations
|
|
739
755
|
audioQueue.sync {
|
|
756
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
757
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
740
758
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
741
759
|
call.reject("Failed to get audio asset")
|
|
742
760
|
return
|
|
@@ -752,6 +770,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
752
770
|
|
|
753
771
|
@objc func getDuration(_ call: CAPPluginCall) {
|
|
754
772
|
audioQueue.sync {
|
|
773
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
774
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
755
775
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
756
776
|
call.reject("Failed to get audio asset")
|
|
757
777
|
return
|
|
@@ -765,6 +785,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
765
785
|
|
|
766
786
|
@objc func getCurrentTime(_ call: CAPPluginCall) {
|
|
767
787
|
audioQueue.sync {
|
|
788
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
789
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
768
790
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
769
791
|
call.reject("Failed to get audio asset")
|
|
770
792
|
return
|
|
@@ -777,7 +799,10 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
777
799
|
}
|
|
778
800
|
|
|
779
801
|
@objc func resume(_ call: CAPPluginCall) {
|
|
802
|
+
let audioId = call.getString(Constant.AssetIdKey) ?? ""
|
|
780
803
|
audioQueue.sync {
|
|
804
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
805
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
781
806
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
782
807
|
call.reject("Failed to get audio asset")
|
|
783
808
|
return
|
|
@@ -807,7 +832,7 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
807
832
|
|
|
808
833
|
// Update notification when resumed
|
|
809
834
|
if self.showNotification {
|
|
810
|
-
self.
|
|
835
|
+
self.updateNowPlayingInfo(audioId: audioId, audioAsset: audioAsset)
|
|
811
836
|
}
|
|
812
837
|
|
|
813
838
|
call.resolve()
|
|
@@ -816,6 +841,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
816
841
|
|
|
817
842
|
@objc func pause(_ call: CAPPluginCall) {
|
|
818
843
|
audioQueue.sync {
|
|
844
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
845
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
819
846
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
820
847
|
call.reject("Failed to get audio asset")
|
|
821
848
|
return
|
|
@@ -853,6 +880,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
853
880
|
let fadeOutDuration = call.getDouble(Constant.FadeOutDuration) ?? Double(Constant.DefaultFadeDuration)
|
|
854
881
|
|
|
855
882
|
audioQueue.sync {
|
|
883
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
884
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
856
885
|
guard !self.audioList.isEmpty else {
|
|
857
886
|
call.reject("Audio list is empty")
|
|
858
887
|
return
|
|
@@ -861,9 +890,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
861
890
|
do {
|
|
862
891
|
try self.stopAudio(audioId: audioId, fadeOut: fadeOut, fadeOutDuration: fadeOutDuration)
|
|
863
892
|
|
|
864
|
-
//
|
|
865
|
-
if self.
|
|
866
|
-
self.clearNowPlayingInfo()
|
|
893
|
+
// Reset current track when stopping the asset that was playing (internal state, not just for notifications)
|
|
894
|
+
if self.currentlyPlayingAssetId == audioId {
|
|
867
895
|
self.currentlyPlayingAssetId = nil
|
|
868
896
|
}
|
|
869
897
|
|
|
@@ -883,6 +911,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
883
911
|
|
|
884
912
|
@objc func loop(_ call: CAPPluginCall) {
|
|
885
913
|
audioQueue.sync {
|
|
914
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
915
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
886
916
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
887
917
|
call.reject("Failed to get audio asset")
|
|
888
918
|
return
|
|
@@ -909,6 +939,11 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
909
939
|
asset.unload()
|
|
910
940
|
self.audioList[audioId] = nil
|
|
911
941
|
|
|
942
|
+
// Reset current track if this was the currently playing asset (internal state tracking)
|
|
943
|
+
if self.currentlyPlayingAssetId == audioId {
|
|
944
|
+
self.currentlyPlayingAssetId = nil
|
|
945
|
+
}
|
|
946
|
+
|
|
912
947
|
// Clean up playOnce tracking if this was a playOnce asset
|
|
913
948
|
if self.playOnceAssets.contains(audioId) {
|
|
914
949
|
self.playOnceAssets.remove(audioId)
|
|
@@ -936,6 +971,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
936
971
|
|
|
937
972
|
@objc func setVolume(_ call: CAPPluginCall) {
|
|
938
973
|
audioQueue.sync {
|
|
974
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
975
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
939
976
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
940
977
|
call.reject("Failed to get audio asset")
|
|
941
978
|
return
|
|
@@ -950,6 +987,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
950
987
|
|
|
951
988
|
@objc func setRate(_ call: CAPPluginCall) {
|
|
952
989
|
audioQueue.sync {
|
|
990
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
991
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
953
992
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
954
993
|
call.reject("Failed to get audio asset")
|
|
955
994
|
return
|
|
@@ -963,6 +1002,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
963
1002
|
|
|
964
1003
|
@objc func isPlaying(_ call: CAPPluginCall) {
|
|
965
1004
|
audioQueue.sync {
|
|
1005
|
+
self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: true)
|
|
1006
|
+
defer { self.audioQueue.setSpecific(key: self.audioQueueContextKey, value: nil) }
|
|
966
1007
|
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
|
|
967
1008
|
call.reject("Failed to get audio asset")
|
|
968
1009
|
return
|
|
@@ -1320,6 +1361,8 @@ public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
|
1320
1361
|
}
|
|
1321
1362
|
}
|
|
1322
1363
|
|
|
1364
|
+
/// Clears the Now Playing info. Only used when tearing down (deinit); stop/unload do not clear
|
|
1365
|
+
/// so that the next play can overwrite the notification without a race.
|
|
1323
1366
|
private func clearNowPlayingInfo() {
|
|
1324
1367
|
DispatchQueue.main.async {
|
|
1325
1368
|
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
|