diff --git a/Model/Player/Backends/PlayerBackend.swift b/Model/Player/Backends/PlayerBackend.swift index d5130799..e9a11fd7 100644 --- a/Model/Player/Backends/PlayerBackend.swift +++ b/Model/Player/Backends/PlayerBackend.swift @@ -154,9 +154,14 @@ extension PlayerBackend { let nonHLSStreams = streams.filter { let isHLS = $0.kind == .hls // Check if the stream's resolution is within the maximum allowed resolution - let isWithinResolution = $0.resolution.map { $0 <= maxResolution.value } ?? false + // Safety: Ensure resolution exists before comparing + guard let streamResolution = $0.resolution else { + logger.info("Stream ID: \($0.id) has nil resolution, skipping") + return false + } + let isWithinResolution = streamResolution <= maxResolution.value - logger.info("Stream ID: \($0.id) - Kind: \(String(describing: $0.kind)) - Resolution: \(String(describing: $0.resolution)) - Bitrate: \($0.bitrate ?? 0)") + logger.info("Stream ID: \($0.id) - Kind: \(String(describing: $0.kind)) - Resolution: \(String(describing: streamResolution)) - Bitrate: \($0.bitrate ?? 0)") logger.info("Is HLS: \(isHLS), Is within resolution: \(isWithinResolution)") logger.info("video url: \($0.videoAsset?.url.absoluteString ?? "nil"), audio url: \($0.audioAsset?.url.absoluteString ?? "nil")") @@ -191,8 +196,13 @@ extension PlayerBackend { } let filteredStreams = adjustedStreams.filter { stream in + // Safety check: Ensure stream has a resolution + guard let streamResolution = stream.resolution else { + logger.info("Filtered stream ID: \(stream.id) has nil resolution, excluding") + return false + } // Check if the stream's resolution is within the maximum allowed resolution - let isWithinResolution = stream.resolution <= maxResolution.value + let isWithinResolution = streamResolution <= maxResolution.value logger.info("Filtered stream ID: \(stream.id) - Is within max resolution: \(isWithinResolution)") return isWithinResolution } @@ -200,7 +210,15 @@ extension PlayerBackend { logger.info("Filtered streams count after adjustments: \(filteredStreams.count)") let bestStream = filteredStreams.max { lhs, rhs in - if lhs.resolution == rhs.resolution { + // Safety check: Ensure both streams have resolutions + guard let lhsResolution = lhs.resolution, let rhsResolution = rhs.resolution else { + logger.info("One or both streams missing resolution - LHS: \(lhs.id), RHS: \(rhs.id)") + // If lhs has no resolution, it's "less than" rhs (prefer rhs) + // If rhs has no resolution, it's "less than" lhs (prefer lhs) + return lhs.resolution == nil + } + + if lhsResolution == rhsResolution { guard let lhsFormat = QualityProfile.Format(rawValue: lhs.format.rawValue), let rhsFormat = QualityProfile.Format(rawValue: rhs.format.rawValue) else { @@ -216,9 +234,9 @@ extension PlayerBackend { return lhsFormatIndex > rhsFormatIndex } - logger.info("Comparing resolutions for streams \(lhs.id) and \(rhs.id) - LHS Resolution: \(String(describing: lhs.resolution)), RHS Resolution: \(String(describing: rhs.resolution))") + logger.info("Comparing resolutions for streams \(lhs.id) and \(rhs.id) - LHS Resolution: \(String(describing: lhsResolution)), RHS Resolution: \(String(describing: rhsResolution))") - return lhs.resolution < rhs.resolution + return lhsResolution < rhsResolution } logger.info("Best stream selected: \(String(describing: bestStream?.id)) with resolution: \(String(describing: bestStream?.resolution)) and format: \(String(describing: bestStream?.format))") diff --git a/Model/Player/PlayerQueue.swift b/Model/Player/PlayerQueue.swift index e75bf1fe..8e2ad023 100644 --- a/Model/Player/PlayerQueue.swift +++ b/Model/Player/PlayerQueue.swift @@ -52,7 +52,7 @@ extension PlayerModel { func playItem(_ item: PlayerQueueItem, at time: CMTime? = nil) { advancing = false - if !playingInPictureInPicture, !currentItem.isNil { + if !playingInPictureInPicture, !currentItem.isNil, backend != nil { backend.closeItem() } @@ -125,6 +125,12 @@ extension PlayerModel { } var streamByQualityProfile: Stream? { + // Safety check: Ensure backend is available + guard backend != nil else { + logger.error("Backend is nil when trying to select stream by quality profile") + return nil + } + let profile = qualityProfile ?? .defaultProfile // First attempt: Filter by both `canPlay` and `isPreferred` @@ -229,7 +235,9 @@ extension PlayerModel { self.removeQueueItems() } - backend.closeItem() + if backend != nil { + backend.closeItem() + } } @discardableResult func enqueueVideo( diff --git a/Model/QualityProfile.swift b/Model/QualityProfile.swift index c731b2da..178da97d 100644 --- a/Model/QualityProfile.swift +++ b/Model/QualityProfile.swift @@ -76,8 +76,13 @@ struct QualityProfile: Hashable, Identifiable, Defaults.Serializable { return true } + // Safety check: Ensure stream has a resolution + guard let streamResolution = stream.resolution else { + return false + } + let defaultResolution = Stream.Resolution.custom(height: 720, refreshRate: 30) - let resolutionMatch = resolution.value ?? defaultResolution >= stream.resolution + let resolutionMatch = resolution.value ?? defaultResolution >= streamResolution if resolutionMatch, formats.contains(.stream), stream.kind == .stream { return true