Podcast: local MP3 cache, AirPods, lock screen controls, background audio
All checks were successful
CI / build-and-deploy (push) Successful in 23s
All checks were successful
CI / build-and-deploy (push) Successful in 23s
- Cache generated MP3 to App Support/podcasts/<hash>.mp3 — re-opening the same article plays instantly with zero backend calls - AVAudioSession .playback/.spokenAudio + .allowBluetoothA2DP — routes audio to AirPods and Bluetooth headsets - MPRemoteCommandCenter play/pause/seek — AirPods controls + lock screen - MPNowPlayingInfoCenter — title and scrubber on lock screen - UIBackgroundModes: audio in Info.plist — playback continues when backgrounded - Auto-play when player is ready Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,14 +67,23 @@ struct ClaudeService: Sendable {
|
||||
return try JSONDecoder().decode(PodcastStatus.self, from: data)
|
||||
}
|
||||
|
||||
func downloadPodcastAudio(jobId: String) async throws -> URL {
|
||||
func downloadPodcastAudio(jobId: String, articleUrl: String) async throws -> URL {
|
||||
let dest = ClaudeService.cachedPodcastURL(for: articleUrl)
|
||||
try FileManager.default.createDirectory(at: dest.deletingLastPathComponent(),
|
||||
withIntermediateDirectories: true)
|
||||
let data = try await get("/v1/podcast/audio/\(jobId)")
|
||||
let dest = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("podcast-\(jobId).mp3")
|
||||
try data.write(to: dest, options: .atomic)
|
||||
return dest
|
||||
}
|
||||
|
||||
static func cachedPodcastURL(for articleUrl: String) -> URL {
|
||||
// djb2 hash — stable, no CryptoKit needed
|
||||
let hash = articleUrl.utf8.reduce(UInt64(5381)) { ($0 &* 31) &+ UInt64($1) }
|
||||
let dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
||||
.appendingPathComponent("podcasts")
|
||||
return dir.appendingPathComponent("\(hash).mp3")
|
||||
}
|
||||
|
||||
// MARK: - HTTP primitives
|
||||
|
||||
private func post(_ path: String, body: [String: Any]) async throws -> Data {
|
||||
|
||||
Reference in New Issue
Block a user