@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
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
}
|