import AVFoundation import SwiftUI /// Audio-Wiedergabe-Button für `audio-front`-Karten. Lädt das File einmal /// per MediaCache, spielt mit AVAudioPlayer ab. struct AudioPlayerButton: View { let mediaId: String @Environment(\.mediaCache) private var mediaCache @State private var player: AVAudioPlayer? @State private var isPlaying = false @State private var failed = false var body: some View { Button { togglePlayback() } label: { HStack(spacing: 12) { Image(systemName: failed ? "speaker.slash.fill" : (isPlaying ? "pause.circle.fill" : "play.circle.fill")) .font(.system(size: 48)) .foregroundStyle(failed ? CardsTheme.error : CardsTheme.primary) Text(failed ? "Audio nicht verfügbar" : (isPlaying ? "Wiedergabe läuft" : "Anhören")) .font(.headline) .foregroundStyle(CardsTheme.foreground) } .frame(maxWidth: .infinity) .padding(20) .background(CardsTheme.surface, in: RoundedRectangle(cornerRadius: 12)) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(CardsTheme.border, lineWidth: 1) ) } .buttonStyle(.plain) .disabled(failed) .task(id: mediaId) { await load() } .onDisappear { player?.stop() isPlaying = false } } private func load() async { guard let cache = mediaCache else { failed = true; return } do { let data = try await cache.data(for: mediaId) #if canImport(UIKit) try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default) try AVAudioSession.sharedInstance().setActive(true) #endif player = try AVAudioPlayer(data: data) player?.prepareToPlay() } catch { failed = true } } private func togglePlayback() { guard let player else { return } if player.isPlaying { player.pause() isPlaying = false } else { player.currentTime = 0 player.play() isPlaying = true } } }