@neoskola/auto-play 0.3.2 → 0.3.3
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.
|
@@ -19,9 +19,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
19
19
|
private var didFinishObserver: NSObjectProtocol?
|
|
20
20
|
private var statusObservation: NSKeyValueObservation?
|
|
21
21
|
private var completionFired = false
|
|
22
|
-
private var downloadTask: URLSessionDownloadTask?
|
|
23
|
-
private var localAudioFileURL: URL?
|
|
24
|
-
private var pendingStartFrom: Double = 0
|
|
25
22
|
|
|
26
23
|
var autoDismissMs: Double? {
|
|
27
24
|
return config.autoDismissMs
|
|
@@ -60,70 +57,22 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
60
57
|
|
|
61
58
|
@MainActor
|
|
62
59
|
func playAudio(url: String, startFrom: Double) -> Bool {
|
|
63
|
-
// Clean up any existing player
|
|
64
60
|
cleanupPlayer()
|
|
65
61
|
completionFired = false
|
|
66
62
|
lastReportedSecond = Int(startFrom)
|
|
67
|
-
pendingStartFrom = startFrom
|
|
68
63
|
|
|
69
64
|
guard let audioURL = URL(string: url) else {
|
|
70
65
|
print("[NowPlayingTemplate] Invalid audio URL: \(url)")
|
|
71
66
|
return false
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
// Ensure AVAudioSession is active
|
|
75
69
|
NowPlayingSessionManager.shared.ensureSessionActive()
|
|
76
|
-
|
|
77
|
-
// Update NowPlaying UI immediately (before download completes)
|
|
78
70
|
config.isPlaying = true
|
|
79
71
|
updateNowPlayingInfo()
|
|
80
72
|
MPNowPlayingInfoCenter.default().playbackState = .playing
|
|
81
73
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Download file first, then play from local — avoids FigFilePlayer streaming errors
|
|
85
|
-
downloadTask = URLSession.shared.downloadTask(with: audioURL) { [weak self] tempURL, response, error in
|
|
86
|
-
guard let self = self else { return }
|
|
87
|
-
|
|
88
|
-
if let error = error {
|
|
89
|
-
print("[NowPlayingTemplate] Download failed: \(error.localizedDescription)")
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
guard let tempURL = tempURL else {
|
|
94
|
-
print("[NowPlayingTemplate] Download returned no file")
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Move to a persistent temp location (URLSession deletes the file after this block)
|
|
99
|
-
let localURL = FileManager.default.temporaryDirectory
|
|
100
|
-
.appendingPathComponent("carplay_audio_\(UUID().uuidString).mp3")
|
|
101
|
-
|
|
102
|
-
do {
|
|
103
|
-
// Remove old file if exists
|
|
104
|
-
if let oldFile = self.localAudioFileURL {
|
|
105
|
-
try? FileManager.default.removeItem(at: oldFile)
|
|
106
|
-
}
|
|
107
|
-
try FileManager.default.moveItem(at: tempURL, to: localURL)
|
|
108
|
-
self.localAudioFileURL = localURL
|
|
109
|
-
print("[NowPlayingTemplate] Audio downloaded to: \(localURL.lastPathComponent)")
|
|
110
|
-
} catch {
|
|
111
|
-
print("[NowPlayingTemplate] Failed to move downloaded file: \(error)")
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Start playback on main thread
|
|
116
|
-
DispatchQueue.main.async { [weak self] in
|
|
117
|
-
self?.startPlaybackFromLocalFile(localURL: localURL, startFrom: self?.pendingStartFrom ?? 0)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
downloadTask?.resume()
|
|
121
|
-
|
|
122
|
-
return true
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private func startPlaybackFromLocalFile(localURL: URL, startFrom: Double) {
|
|
126
|
-
let asset = AVURLAsset(url: localURL)
|
|
74
|
+
// Direct AVPlayer streaming — URL now has correct headers from backend
|
|
75
|
+
let asset = AVURLAsset(url: audioURL)
|
|
127
76
|
playerItem = AVPlayerItem(asset: asset)
|
|
128
77
|
|
|
129
78
|
// KVO: detect duration as soon as asset loads (critical for progress bar)
|
|
@@ -153,6 +102,8 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
153
102
|
player?.seek(to: time)
|
|
154
103
|
}
|
|
155
104
|
|
|
105
|
+
player?.play()
|
|
106
|
+
|
|
156
107
|
// Periodic time observer (every 1 second) for MPNowPlayingInfoCenter updates
|
|
157
108
|
let interval = CMTime(seconds: 1.0, preferredTimescale: 600)
|
|
158
109
|
timeObserver = player?.addPeriodicTimeObserver(
|
|
@@ -162,15 +113,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
162
113
|
self?.handleTimeUpdate(time: time)
|
|
163
114
|
}
|
|
164
115
|
|
|
165
|
-
// Playback finished notification
|
|
166
|
-
didFinishObserver = NotificationCenter.default.addObserver(
|
|
167
|
-
forName: .AVPlayerItemDidPlayToEndTime,
|
|
168
|
-
object: playerItem,
|
|
169
|
-
queue: .main
|
|
170
|
-
) { [weak self] _ in
|
|
171
|
-
self?.handlePlaybackFinished()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
116
|
// Progress report timer (every 30 seconds) — calls JS callback for backend reporting
|
|
175
117
|
progressReportTimer = Timer.scheduledTimer(
|
|
176
118
|
withTimeInterval: 30.0,
|
|
@@ -179,11 +121,17 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
179
121
|
self?.reportProgress()
|
|
180
122
|
}
|
|
181
123
|
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
124
|
+
// Playback finished notification
|
|
125
|
+
didFinishObserver = NotificationCenter.default.addObserver(
|
|
126
|
+
forName: .AVPlayerItemDidPlayToEndTime,
|
|
127
|
+
object: playerItem,
|
|
128
|
+
queue: .main
|
|
129
|
+
) { [weak self] _ in
|
|
130
|
+
self?.handlePlaybackFinished()
|
|
131
|
+
}
|
|
185
132
|
|
|
186
|
-
print("[NowPlayingTemplate]
|
|
133
|
+
print("[NowPlayingTemplate] Streaming playback started: \(url)")
|
|
134
|
+
return true
|
|
187
135
|
}
|
|
188
136
|
|
|
189
137
|
private func handleTimeUpdate(time: CMTime) {
|
|
@@ -282,8 +230,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
282
230
|
}
|
|
283
231
|
|
|
284
232
|
private func cleanupPlayer() {
|
|
285
|
-
downloadTask?.cancel()
|
|
286
|
-
downloadTask = nil
|
|
287
233
|
statusObservation?.invalidate()
|
|
288
234
|
statusObservation = nil
|
|
289
235
|
if let timeObserver = timeObserver {
|
|
@@ -299,11 +245,6 @@ class NowPlayingTemplate: AutoPlayTemplate {
|
|
|
299
245
|
player?.pause()
|
|
300
246
|
player = nil
|
|
301
247
|
playerItem = nil
|
|
302
|
-
// Clean up temp audio file
|
|
303
|
-
if let localFile = localAudioFileURL {
|
|
304
|
-
try? FileManager.default.removeItem(at: localFile)
|
|
305
|
-
localAudioFileURL = nil
|
|
306
|
-
}
|
|
307
248
|
}
|
|
308
249
|
|
|
309
250
|
// MARK: - CarPlay UI
|