import AVFoundation import Foundation @Observable final class PlayerViewModel { var player: AVPlayer? var isPlaying = false var currentTime: Double = 0 var duration: Double = 0 var isLoading = false var error: String? var playbackRate: Float = 1.0 private var timeObserver: Any? private var contentId: String = "" private var episodeId: Int = 0 var progress: Double { guard duration > 0 else { return 0 } return currentTime / duration } var currentTimeText: String { formatTime(currentTime) } var durationText: String { formatTime(duration) } func play(url: String, contentId: String, episodeId: Int = 0) { self.contentId = contentId self.episodeId = episodeId guard let videoURL = URL(string: url) else { error = "无效的视频地址" return } let playerItem = AVPlayerItem(url: videoURL) player = AVPlayer(playerItem: playerItem) player?.rate = playbackRate // 恢复上次进度 if let savedProgress = WatchProgressStore.shared.getProgress(for: contentId, episodeId: episodeId) { let resumeTime = CMTime(seconds: savedProgress.currentTime, preferredTimescale: 600) player?.seek(to: resumeTime) } setupTimeObserver() player?.play() isPlaying = true } func togglePlay() { guard let player else { return } if isPlaying { player.pause() } else { player.play() } isPlaying.toggle() } func seek(to time: Double) { let cmTime = CMTime(seconds: time, preferredTimescale: 600) player?.seek(to: cmTime) } func seekForward(_ seconds: Double = 10) { seek(to: min(currentTime + seconds, duration)) } func seekBackward(_ seconds: Double = 10) { seek(to: max(currentTime - seconds, 0)) } func setRate(_ rate: Float) { playbackRate = rate player?.rate = rate } func stop() { saveProgress() player?.pause() if let observer = timeObserver { player?.removeTimeObserver(observer) } timeObserver = nil player = nil isPlaying = false } private func setupTimeObserver() { let interval = CMTime(seconds: 1, preferredTimescale: 600) timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in guard let self else { return } self.currentTime = time.seconds if let item = self.player?.currentItem { self.duration = item.duration.seconds.isNaN ? 0 : item.duration.seconds } // 每 10 秒保存一次进度 if Int(self.currentTime) % 10 == 0 { self.saveProgress() } } } private func saveProgress() { guard duration > 0 else { return } WatchProgressStore.shared.saveProgress( contentId: contentId, episodeId: episodeId, currentTime: currentTime, duration: duration ) } private func formatTime(_ seconds: Double) -> String { guard !seconds.isNaN, seconds >= 0 else { return "00:00" } let totalSeconds = Int(seconds) let hours = totalSeconds / 3600 let minutes = (totalSeconds % 3600) / 60 let secs = totalSeconds % 60 if hours > 0 { return String(format: "%d:%02d:%02d", hours, minutes, secs) } return String(format: "%02d:%02d", minutes, secs) } deinit { stop() } }