capacitor-plugin-playlist 0.1.18 → 0.1.22
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/src/main/java/org/dwbn/plugins/playlist/PlaylistPlugin.kt +4 -8
- package/android/src/main/java/org/dwbn/plugins/playlist/RmxAudioPlayer.java +397 -385
- package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaylistManager.kt +17 -12
- package/android/src/main/java/org/dwbn/plugins/playlist/notification/PlaylistNotificationProvider.kt +5 -1
- package/android/src/main/java/org/dwbn/plugins/playlist/service/MediaService.kt +1 -2
- package/dist/docs.json +5 -23
- package/dist/esm/Constants.js.map +1 -1
- package/dist/esm/RmxAudioPlayer.d.ts +4 -10
- package/dist/esm/RmxAudioPlayer.js +19 -22
- package/dist/esm/RmxAudioPlayer.js.map +1 -1
- package/dist/esm/definitions.d.ts +5 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/plugin.js +1 -0
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/utils.d.ts +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/web.d.ts +3 -3
- package/dist/esm/web.js +32 -27
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +47 -44
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +48 -45
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/AVBidirectionalQueuePlayer.swift +18 -4
- package/ios/Plugin/AudioTrack.swift +0 -1
- package/ios/Plugin/DispatchQueue.swift +3 -3
- package/ios/Plugin/RmxAudioPlayer.swift +77 -108
- package/package.json +1 -1
- package/android/.gitignore +0 -1
- package/android/.npmignore +0 -1
- package/ios/.DS_Store +0 -0
|
@@ -12,7 +12,7 @@ public extension DispatchQueue {
|
|
|
12
12
|
- action: The closure to be executed
|
|
13
13
|
Delays a closure execution and ensures no other executions are made during deadline
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
func throttle(deadline: DispatchTime, context: AnyHashable? = nil, action: @escaping () -> Void) {
|
|
16
16
|
let worker = DispatchWorkItem {
|
|
17
17
|
defer { throttleWorkItems.removeValue(forKey: context ?? nilContext) }
|
|
18
18
|
action()
|
|
@@ -31,7 +31,7 @@ public extension DispatchQueue {
|
|
|
31
31
|
- action: The closure to be executed
|
|
32
32
|
Executes a closure and ensures no other executions will be made during the interval.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
func debounce(interval: Double, context: AnyHashable? = nil, action: @escaping () -> Void) {
|
|
35
35
|
if let last = lastDebounceCallTimes[context ?? nilContext], last + interval > .now() {
|
|
36
36
|
return
|
|
37
37
|
}
|
|
@@ -44,4 +44,4 @@ public extension DispatchQueue {
|
|
|
44
44
|
lastDebounceCallTimes.removeValue(forKey: context ?? nilContext)
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|
|
@@ -29,6 +29,8 @@ final class RmxAudioPlayer: NSObject {
|
|
|
29
29
|
|
|
30
30
|
private let avQueuePlayer = AVBidirectionalQueuePlayer(items: [])
|
|
31
31
|
|
|
32
|
+
private var lastTrackId: String? = nil
|
|
33
|
+
private var lastRate: Float? = nil
|
|
32
34
|
override init() {
|
|
33
35
|
super.init()
|
|
34
36
|
|
|
@@ -140,7 +142,7 @@ final class RmxAudioPlayer: NSObject {
|
|
|
140
142
|
|
|
141
143
|
func clearAllItems() {
|
|
142
144
|
print("RmxAudioPlayer.execute=clearAllItems")
|
|
143
|
-
removeAllTracks(
|
|
145
|
+
removeAllTracks()
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
func playTrack(index: Int, positionTime: Float?) throws {
|
|
@@ -385,7 +387,7 @@ final class RmxAudioPlayer: NSObject {
|
|
|
385
387
|
}
|
|
386
388
|
}
|
|
387
389
|
|
|
388
|
-
func removeAllTracks(
|
|
390
|
+
func removeAllTracks() {
|
|
389
391
|
for item in avQueuePlayer.queuedAudioTracks {
|
|
390
392
|
removeTrackObservers(item)
|
|
391
393
|
}
|
|
@@ -393,15 +395,6 @@ final class RmxAudioPlayer: NSObject {
|
|
|
393
395
|
avQueuePlayer.removeAllItems()
|
|
394
396
|
wasPlayingInterrupted = false
|
|
395
397
|
|
|
396
|
-
print("RmxAudioPlayer, removeAllTracks, ==> RMXSTATUS_PLAYLIST_CLEARED")
|
|
397
|
-
onStatus(.rmxstatus_PLAYLIST_CLEARED, trackId: "INVALID", param: nil)
|
|
398
|
-
|
|
399
|
-
// a.t.m there's no way for this to be triggered from within the plugin,
|
|
400
|
-
// but it might get added at some point.
|
|
401
|
-
if isCommand {
|
|
402
|
-
let action = "music-controls-clear"
|
|
403
|
-
print("\(action)")
|
|
404
|
-
}
|
|
405
398
|
}
|
|
406
399
|
|
|
407
400
|
// MARK: - remote control events
|
|
@@ -454,7 +447,6 @@ final class RmxAudioPlayer: NSObject {
|
|
|
454
447
|
let trackStatus = getStatusItem(playerItem)
|
|
455
448
|
|
|
456
449
|
onStatus(.rmxstatus_STALLED, trackId: playerItem?.trackId, param: trackStatus)
|
|
457
|
-
onStatus(.rmxstatus_PAUSE, trackId: playerItem?.trackId, param: trackStatus)
|
|
458
450
|
}
|
|
459
451
|
|
|
460
452
|
@objc func playerItemDidReachEnd(_ notification: Notification?) {
|
|
@@ -521,7 +513,6 @@ final class RmxAudioPlayer: NSObject {
|
|
|
521
513
|
if avQueuePlayer.isPlaying {
|
|
522
514
|
let trackStatus = getStatusItem(playerItem)
|
|
523
515
|
onStatus(.rmxstatus_PLAYBACK_POSITION, trackId: playerItem.trackId, param: trackStatus)
|
|
524
|
-
// NSLog(@" . %.5f / %.5f sec (%.1f %%) [%@]", currentTime, duration, (currentTime / duration)*100.0, name);
|
|
525
516
|
}
|
|
526
517
|
}
|
|
527
518
|
|
|
@@ -529,50 +520,55 @@ final class RmxAudioPlayer: NSObject {
|
|
|
529
520
|
}
|
|
530
521
|
|
|
531
522
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
523
|
+
guard let change = change else {
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
switch keyPath {
|
|
528
|
+
case "currentItem":
|
|
529
|
+
// only fire on real change!
|
|
535
530
|
let player = object as? AVBidirectionalQueuePlayer
|
|
536
531
|
let playerItem = player?.currentAudioTrack
|
|
532
|
+
guard lastTrackId != playerItem?.trackId else {
|
|
533
|
+
return // todo should call super instead or?
|
|
534
|
+
}
|
|
535
|
+
lastTrackId = playerItem?.trackId
|
|
537
536
|
handleCurrentItemChanged(playerItem)
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
guard let playerItem = player?.currentAudioTrack else { return }
|
|
546
|
-
|
|
547
|
-
let trackStatus = getStatusItem(playerItem)
|
|
548
|
-
print("Playback rate changed: \(1), is playing: \(player?.isPlaying ?? false)")
|
|
549
|
-
|
|
550
|
-
if player?.isPlaying ?? false {
|
|
551
|
-
onStatus(.rmxstatus_PLAYING, trackId: playerItem.trackId, param: trackStatus)
|
|
552
|
-
} else {
|
|
553
|
-
onStatus(.rmxstatus_PAUSE, trackId: playerItem.trackId, param: trackStatus)
|
|
554
|
-
}
|
|
555
|
-
})
|
|
537
|
+
case "rate":
|
|
538
|
+
guard lastRate != change[.newKey] as? Float else {
|
|
539
|
+
return // todo should call super instead or?
|
|
540
|
+
}
|
|
541
|
+
self.lastRate = change[.newKey] as? Float
|
|
542
|
+
let player = object as? AVBidirectionalQueuePlayer
|
|
556
543
|
|
|
557
|
-
return
|
|
558
|
-
|
|
544
|
+
guard let playerItem = player?.currentAudioTrack else { return }
|
|
545
|
+
|
|
546
|
+
let trackStatus = getStatusItem(playerItem)
|
|
547
|
+
print("Playback rate changed: \(String(describing: change[.newKey])), is playing: \(player?.isPlaying ?? false)")
|
|
559
548
|
|
|
560
|
-
|
|
549
|
+
if player?.isPlaying ?? false {
|
|
550
|
+
onStatus(.rmxstatus_PLAYING, trackId: playerItem.trackId, param: trackStatus)
|
|
551
|
+
} else {
|
|
552
|
+
onStatus(.rmxstatus_PAUSE, trackId: playerItem.trackId, param: trackStatus)
|
|
553
|
+
}
|
|
554
|
+
case "status":
|
|
561
555
|
DispatchQueue.main.debounce(interval: 0.2, context: self, action: { [self] in
|
|
562
556
|
let playerItem = object as? AudioTrack
|
|
563
557
|
handleTrackStatusEvent(playerItem)
|
|
564
558
|
})
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (keyPath == "timeControlStatus") {
|
|
559
|
+
case "timeControlStatus":
|
|
569
560
|
DispatchQueue.main.debounce(interval: 0.2, context: self, action: { [self] in
|
|
570
561
|
let player = object as? AVBidirectionalQueuePlayer
|
|
571
562
|
|
|
572
|
-
guard let playerItem = player?.currentAudioTrack else {
|
|
563
|
+
guard let playerItem = player?.currentAudioTrack else {
|
|
564
|
+
return
|
|
565
|
+
}
|
|
566
|
+
guard lastTrackId != playerItem.trackId else {
|
|
567
|
+
return // todo should call super instead or?
|
|
568
|
+
}
|
|
573
569
|
|
|
574
570
|
let trackStatus = getStatusItem(playerItem)
|
|
575
|
-
print("
|
|
571
|
+
print("TCSPlayback rate changed: \(String(describing: change[.newKey])), is playing: \(player?.isPlaying ?? false)")
|
|
576
572
|
|
|
577
573
|
if player?.timeControlStatus == .playing {
|
|
578
574
|
onStatus(.rmxstatus_PLAYING, trackId: playerItem.trackId, param: trackStatus)
|
|
@@ -582,36 +578,28 @@ final class RmxAudioPlayer: NSObject {
|
|
|
582
578
|
onStatus(.rmxstatus_PAUSE, trackId: playerItem.trackId, param: trackStatus)
|
|
583
579
|
}
|
|
584
580
|
})
|
|
585
|
-
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
if (keyPath == "duration") {
|
|
581
|
+
case "duration":
|
|
589
582
|
DispatchQueue.main.debounce(interval: 0.5, context: self, action: { [self] in
|
|
590
583
|
let playerItem = object as? AudioTrack
|
|
591
584
|
handleTrackDuration(playerItem)
|
|
592
585
|
})
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (keyPath == "loadedTimeRanges") {
|
|
586
|
+
case "loadedTimeRanges":
|
|
597
587
|
DispatchQueue.main.debounce(interval: 0.2, context: self, action: { [self] in
|
|
598
588
|
let playerItem = object as? AudioTrack
|
|
599
589
|
handleTrackBuffering(playerItem)
|
|
600
590
|
})
|
|
601
|
-
|
|
591
|
+
default:
|
|
592
|
+
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
|
602
593
|
}
|
|
603
|
-
|
|
604
|
-
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
|
605
|
-
return
|
|
606
594
|
}
|
|
607
595
|
|
|
608
596
|
func updateNowPlayingTrackInfo(_ playerItem: AudioTrack?, updateTrackData: Bool) {
|
|
597
|
+
|
|
609
598
|
let currentItem = playerItem ?? avQueuePlayer.currentAudioTrack
|
|
610
|
-
|
|
611
599
|
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
|
|
612
600
|
if updatedNowPlayingInfo == nil {
|
|
613
601
|
let nowPlayingInfo = nowPlayingInfoCenter.nowPlayingInfo
|
|
614
|
-
updatedNowPlayingInfo = nowPlayingInfo
|
|
602
|
+
updatedNowPlayingInfo = nowPlayingInfo ?? [:]
|
|
615
603
|
}
|
|
616
604
|
|
|
617
605
|
var currentTime: Float? = nil
|
|
@@ -627,70 +615,52 @@ final class RmxAudioPlayer: NSObject {
|
|
|
627
615
|
}
|
|
628
616
|
|
|
629
617
|
if updateTrackData {
|
|
630
|
-
updatedNowPlayingInfo
|
|
631
|
-
updatedNowPlayingInfo
|
|
632
|
-
updatedNowPlayingInfo
|
|
633
|
-
let mediaItemArtwork = createCoverArtwork(currentItem?.albumArt?.absoluteString)
|
|
618
|
+
updatedNowPlayingInfo![MPMediaItemPropertyArtist] = currentItem?.artist
|
|
619
|
+
updatedNowPlayingInfo![MPMediaItemPropertyTitle] = currentItem?.title
|
|
620
|
+
updatedNowPlayingInfo![MPMediaItemPropertyAlbumTitle] = currentItem?.album
|
|
634
621
|
|
|
635
|
-
if let mediaItemArtwork =
|
|
636
|
-
updatedNowPlayingInfo
|
|
622
|
+
if let mediaItemArtwork = createCoverArtwork(currentItem?.albumArt?.absoluteString) {
|
|
623
|
+
updatedNowPlayingInfo![MPMediaItemPropertyArtwork] = mediaItemArtwork
|
|
637
624
|
}
|
|
638
625
|
}
|
|
626
|
+
updatedNowPlayingInfo![MPMediaItemPropertyPlaybackDuration] = duration ?? 0.0
|
|
627
|
+
updatedNowPlayingInfo![MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime ?? 0.0
|
|
628
|
+
updatedNowPlayingInfo![MPNowPlayingInfoPropertyPlaybackRate] = 1.0
|
|
639
629
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
updatedNowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = NSNumber(value: 1.0)
|
|
643
|
-
|
|
644
|
-
nowPlayingInfoCenter.nowPlayingInfo = updatedNowPlayingInfo
|
|
645
|
-
|
|
630
|
+
MPNowPlayingInfoCenter.default().nowPlayingInfo = updatedNowPlayingInfo
|
|
631
|
+
|
|
646
632
|
let commandCenter = MPRemoteCommandCenter.shared()
|
|
647
633
|
commandCenter.nextTrackCommand.isEnabled = !avQueuePlayer.isAtEnd
|
|
648
634
|
commandCenter.previousTrackCommand.isEnabled = !avQueuePlayer.isAtBeginning
|
|
649
635
|
}
|
|
650
636
|
|
|
651
|
-
func createCoverArtwork(_
|
|
652
|
-
|
|
653
|
-
var coverImage: UIImage? = nil
|
|
654
|
-
if coverUri == nil {
|
|
637
|
+
func createCoverArtwork(_ coverUriOrNil: String?) -> MPMediaItemArtwork? {
|
|
638
|
+
guard let coverUri = coverUriOrNil else {
|
|
655
639
|
return nil
|
|
656
640
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
if FileManager.default.fileExists(atPath: fullCoverImagePath ?? "") {
|
|
662
|
-
coverImage = UIImage(contentsOfFile: fullCoverImagePath ?? "")
|
|
663
|
-
}
|
|
664
|
-
} else if coverUri?.hasPrefix("http://") ?? false || coverUri?.hasPrefix("https://") ?? false {
|
|
665
|
-
let coverImageUrl = URL(string: coverUri ?? "")
|
|
641
|
+
|
|
642
|
+
var coverImage: UIImage? = nil
|
|
643
|
+
if coverUri.hasPrefix("http://") || coverUri.hasPrefix("https://") {
|
|
644
|
+
let coverImageUrl = URL(string: coverUri)!
|
|
666
645
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
do {
|
|
670
|
-
coverImageData = try Data(contentsOf: coverImageUrl)
|
|
671
|
-
} catch {
|
|
672
|
-
print("Error creating the coverImageData");
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
if let coverImageData = coverImageData {
|
|
646
|
+
do {
|
|
647
|
+
let coverImageData = try Data(contentsOf: coverImageUrl)
|
|
676
648
|
coverImage = UIImage(data: coverImageData)
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
let baseCoverImagePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).map(\.path)[0]
|
|
680
|
-
let fullCoverImagePath = "\(baseCoverImagePath)\(coverUri ?? "")"
|
|
681
|
-
if FileManager.default.fileExists(atPath: fullCoverImagePath) {
|
|
682
|
-
coverImage = UIImage(named: fullCoverImagePath)
|
|
649
|
+
} catch {
|
|
650
|
+
print("Error creating the coverImageData");
|
|
683
651
|
}
|
|
684
652
|
} else {
|
|
685
|
-
|
|
653
|
+
if FileManager.default.fileExists(atPath: coverUri) {
|
|
654
|
+
coverImage = UIImage(contentsOfFile: coverUri)
|
|
655
|
+
}
|
|
686
656
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
657
|
+
|
|
658
|
+
if isCoverImageValid(coverImage) {
|
|
659
|
+
return MPMediaItemArtwork.init(boundsSize: coverImage!.size, requestHandler: { (size) -> UIImage in
|
|
660
|
+
return coverImage!
|
|
661
|
+
})
|
|
692
662
|
}
|
|
693
|
-
return nil
|
|
663
|
+
return nil;
|
|
694
664
|
}
|
|
695
665
|
|
|
696
666
|
func downloadImage(url: URL, completion: @escaping ((_ image: UIImage?) -> Void)){
|
|
@@ -720,7 +690,7 @@ final class RmxAudioPlayer: NSObject {
|
|
|
720
690
|
print("New item ID: \(playerItem.trackId ?? "")")
|
|
721
691
|
print("Queue is at end: \(avQueuePlayer.isAtEnd ? "YES" : "NO")")
|
|
722
692
|
// When an item starts, immediately scrub it back to the beginning
|
|
723
|
-
playerItem.seek(to: .zero, toleranceBefore: .zero, toleranceAfter: .zero, completionHandler: nil)
|
|
693
|
+
//playerItem.seek(to: .zero, toleranceBefore: .zero, toleranceAfter: .zero, completionHandler: nil)
|
|
724
694
|
// Update the command center
|
|
725
695
|
updateNowPlayingTrackInfo(playerItem, updateTrackData: true)
|
|
726
696
|
} else if loop {
|
|
@@ -880,7 +850,7 @@ final class RmxAudioPlayer: NSObject {
|
|
|
880
850
|
}
|
|
881
851
|
|
|
882
852
|
if avQueuePlayer.currentItem == currentItem {
|
|
883
|
-
if avQueuePlayer.rate != 0 {
|
|
853
|
+
if avQueuePlayer.rate != 0.0 {
|
|
884
854
|
status = "playing"
|
|
885
855
|
|
|
886
856
|
if position <= 0 && (bufferInfo?["bufferPercent"] as? NSNumber)?.floatValue ?? 0.0 == 0.0 {
|
|
@@ -1010,7 +980,6 @@ final class RmxAudioPlayer: NSObject {
|
|
|
1010
980
|
listener.addObserver(self, selector: #selector(itemStalledPlaying(_:)), name: .AVPlayerItemPlaybackStalled, object: playerItem)
|
|
1011
981
|
|
|
1012
982
|
onStatus(.rmxstatus_ITEM_ADDED, trackId: playerItem?.trackId, param: playerItem?.toDict())
|
|
1013
|
-
onStatus(.rmxstatus_LOADING, trackId: playerItem?.trackId, param: nil)
|
|
1014
983
|
}
|
|
1015
984
|
|
|
1016
985
|
@objc func queueCleared(_ notification: Notification?) {
|
|
@@ -1123,7 +1092,7 @@ final class RmxAudioPlayer: NSObject {
|
|
|
1123
1092
|
avQueuePlayer.removeObserver(self as NSObject, forKeyPath: "timeControlStatus")
|
|
1124
1093
|
deregisterMusicControlsEventListener()
|
|
1125
1094
|
|
|
1126
|
-
removeAllTracks(
|
|
1095
|
+
removeAllTracks()
|
|
1127
1096
|
|
|
1128
1097
|
playbackTimeObserver = nil
|
|
1129
1098
|
isWaitingToStartPlayback = false
|
package/package.json
CHANGED
package/android/.gitignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/build
|
package/android/.npmignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/build
|
package/ios/.DS_Store
DELETED
|
Binary file
|