@neoskola/auto-play 0.3.0 → 0.3.1

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.
@@ -17,6 +17,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
17
17
  private var progressReportTimer: Timer?
18
18
  private var lastReportedSecond: Int = 0
19
19
  private var didFinishObserver: NSObjectProtocol?
20
+ private var statusObservation: NSKeyValueObservation?
20
21
  private var completionFired = false
21
22
 
22
23
  var autoDismissMs: Double? {
@@ -71,6 +72,26 @@ class NowPlayingTemplate: AutoPlayTemplate {
71
72
 
72
73
  let asset = AVURLAsset(url: audioURL)
73
74
  playerItem = AVPlayerItem(asset: asset)
75
+
76
+ // KVO: detect duration as soon as asset loads (critical for progress bar)
77
+ statusObservation = playerItem?.observe(\.status, options: [.new]) { [weak self] item, _ in
78
+ guard item.status == .readyToPlay else {
79
+ if item.status == .failed {
80
+ print("[NowPlayingTemplate] AVPlayerItem failed: \(item.error?.localizedDescription ?? "unknown")")
81
+ }
82
+ return
83
+ }
84
+ DispatchQueue.main.async { [weak self] in
85
+ guard let self = self else { return }
86
+ let duration = CMTimeGetSeconds(item.duration)
87
+ if !duration.isNaN && !duration.isInfinite && duration > 0 {
88
+ self.currentDuration = duration
89
+ self.updateNowPlayingInfo()
90
+ print("[NowPlayingTemplate] Duration resolved via KVO: \(duration)s")
91
+ }
92
+ }
93
+ }
94
+
74
95
  player = AVPlayer(playerItem: playerItem)
75
96
 
76
97
  // Seek to start position
@@ -124,20 +145,29 @@ class NowPlayingTemplate: AutoPlayTemplate {
124
145
  guard !currentTime.isNaN else { return }
125
146
 
126
147
  currentElapsedTime = currentTime
127
- if !duration.isNaN && duration > 0 {
148
+ if !duration.isNaN && !duration.isInfinite && duration > 0 {
128
149
  currentDuration = duration
129
150
  }
130
151
 
131
- // Update MPNowPlayingInfoCenter no JS roundtrip needed
132
- if var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo {
133
- if currentDuration > 0 {
134
- nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentDuration
152
+ // Use existing info or create fresh if nil (race condition safety)
153
+ var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
154
+
155
+ // Ensure title/artist are present if this is a fresh dictionary
156
+ if nowPlayingInfo[MPMediaItemPropertyTitle] == nil {
157
+ let titleText = Parser.parseText(text: config.title) ?? ""
158
+ nowPlayingInfo[MPMediaItemPropertyTitle] = titleText
159
+ if let subtitle = config.subtitle {
160
+ nowPlayingInfo[MPMediaItemPropertyArtist] = Parser.parseText(text: subtitle)
135
161
  }
136
- nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
137
- nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
138
- MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
139
162
  }
140
163
 
164
+ if currentDuration > 0 {
165
+ nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentDuration
166
+ }
167
+ nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
168
+ nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
169
+ MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
170
+
141
171
  // 95% completion check
142
172
  if !completionFired && currentDuration > 0 && currentTime / currentDuration >= 0.95 {
143
173
  completionFired = true
@@ -204,6 +234,8 @@ class NowPlayingTemplate: AutoPlayTemplate {
204
234
  }
205
235
 
206
236
  private func cleanupPlayer() {
237
+ statusObservation?.invalidate()
238
+ statusObservation = nil
207
239
  if let timeObserver = timeObserver {
208
240
  player?.removeTimeObserver(timeObserver)
209
241
  self.timeObserver = nil
@@ -342,6 +374,7 @@ class NowPlayingTemplate: AutoPlayTemplate {
342
374
 
343
375
  // MARK: - AutoPlayTemplate Protocol
344
376
 
377
+ @MainActor
345
378
  func invalidate() {
346
379
  setupNowPlayingButtons()
347
380
  updateNowPlayingInfo()
@@ -398,13 +431,21 @@ class NowPlayingTemplate: AutoPlayTemplate {
398
431
  isSetupComplete = true
399
432
  }
400
433
 
401
- // Update playback rate in existing nowPlayingInfo
402
- if var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo {
403
- nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPlaying ? 1.0 : 0.0
404
- nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
405
- MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
434
+ // Update playback rate use existing info or create fresh if nil
435
+ var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
436
+
437
+ if nowPlayingInfo[MPMediaItemPropertyTitle] == nil {
438
+ let titleText = Parser.parseText(text: config.title) ?? ""
439
+ nowPlayingInfo[MPMediaItemPropertyTitle] = titleText
440
+ if let subtitle = config.subtitle {
441
+ nowPlayingInfo[MPMediaItemPropertyArtist] = Parser.parseText(text: subtitle)
442
+ }
406
443
  }
407
444
 
445
+ nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPlaying ? 1.0 : 0.0
446
+ nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentElapsedTime
447
+ MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
448
+
408
449
  MPNowPlayingInfoCenter.default().playbackState = isPlaying ? .playing : .paused
409
450
  }
410
451
 
@@ -420,12 +461,20 @@ class NowPlayingTemplate: AutoPlayTemplate {
420
461
  self.currentElapsedTime = elapsedTime
421
462
  self.currentDuration = duration
422
463
 
423
- // Update only time-related fields without rebuilding everything
424
- if var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo {
425
- nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration
426
- nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTime
427
- nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
428
- MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
464
+ // Update time-related fields use existing info or create fresh if nil
465
+ var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo ?? [String: Any]()
466
+
467
+ if nowPlayingInfo[MPMediaItemPropertyTitle] == nil {
468
+ let titleText = Parser.parseText(text: config.title) ?? ""
469
+ nowPlayingInfo[MPMediaItemPropertyTitle] = titleText
470
+ if let subtitle = config.subtitle {
471
+ nowPlayingInfo[MPMediaItemPropertyArtist] = Parser.parseText(text: subtitle)
472
+ }
429
473
  }
474
+
475
+ nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration
476
+ nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTime
477
+ nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
478
+ MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
430
479
  }
431
480
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoskola/auto-play",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Android Auto and Apple CarPlay for react-native",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",