From 35534bcbb1d96c57e149a1ceafbfebb0379a3d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20F=C3=B6rster?= Date: Mon, 19 Aug 2024 16:35:29 +0200 Subject: [PATCH] Improved thumbnail handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ThumbnailsModel optionally returns the quality - Have constants for 4:3 and 16:9 aspect ratio - Add high quality options for thumbnails - Rename Highest quality to Best quality - make 4:3 thumbnails fill the VideoCell - use .maxes instead of .maxresdefault (the latter sometimes returns white images) Signed-off-by: Toni Förster --- Model/ThumbnailsModel.swift | 10 ++++--- Shared/Constants.swift | 2 ++ Shared/Defaults.swift | 6 ++-- Shared/Player/Controls/PlayerControls.swift | 2 +- Shared/Player/Video Details/ChapterView.swift | 4 +-- Shared/Player/VideoPlayerView.swift | 2 +- Shared/Videos/VideoBanner.swift | 14 ++++----- Shared/Videos/VideoCell.swift | 30 +++++++------------ 8 files changed, 31 insertions(+), 39 deletions(-) diff --git a/Model/ThumbnailsModel.swift b/Model/ThumbnailsModel.swift index cb6efacd..cb9d173a 100644 --- a/Model/ThumbnailsModel.swift +++ b/Model/ThumbnailsModel.swift @@ -20,21 +20,23 @@ final class ThumbnailsModel: ObservableObject { return unloadable.contains(url) } - func best(_ video: Video) -> URL? { + func best(_ video: Video) -> (url: URL?, quality: Thumbnail.Quality?) { for quality in availableQualitites { let url = video.thumbnailURL(quality: quality) if !isUnloadable(url) { - return url + return (url, quality) } } - return nil + return (nil, nil) } private var availableQualitites: [Thumbnail.Quality] { switch Defaults[.thumbnailsQuality] { case .highest: - return [.maxresdefault, .medium, .default] + return [.maxres, .high, .medium, .default] + case .high: + return [.high, .medium, .default] case .medium: return [.medium, .default] case .low: diff --git a/Shared/Constants.swift b/Shared/Constants.swift index b8fa0b07..75d7d374 100644 --- a/Shared/Constants.swift +++ b/Shared/Constants.swift @@ -4,6 +4,8 @@ import SwiftUI enum Constants { static let overlayAnimation = Animation.linear(duration: 0.2) + static let aspectRatio16x9 = 16.0 / 9.0 + static let aspectRatio4x3 = 4.0 / 3.0 static var isAppleTV: Bool { #if os(iOS) diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index f7b7415f..7995ef38 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -462,12 +462,14 @@ enum ButtonLabelStyle: String, CaseIterable, Defaults.Serializable { } enum ThumbnailsQuality: String, CaseIterable, Defaults.Serializable { - case highest, medium, low + case highest, high, medium, low var description: String { switch self { case .highest: - return "Highest quality".localized() + return "Best quality".localized() + case .high: + return "High quality".localized() case .medium: return "Medium quality".localized() case .low: diff --git a/Shared/Player/Controls/PlayerControls.swift b/Shared/Player/Controls/PlayerControls.swift index 05d2c6e3..c4e5234d 100644 --- a/Shared/Player/Controls/PlayerControls.swift +++ b/Shared/Player/Controls/PlayerControls.swift @@ -265,7 +265,7 @@ struct PlayerControls: View { var controlsBackgroundURL: URL? { if let video = player.videoForDisplay, - let url = thumbnails.best(video) + let url = thumbnails.best(video).url { return url } diff --git a/Shared/Player/Video Details/ChapterView.swift b/Shared/Player/Video Details/ChapterView.swift index f4b9c831..d1b9c495 100644 --- a/Shared/Player/Video Details/ChapterView.swift +++ b/Shared/Player/Video Details/ChapterView.swift @@ -65,7 +65,7 @@ import SwiftUI } static var thumbnailHeight: Double { - thumbnailWidth / (16 / 9) + thumbnailWidth / Constants.aspectRatio16x9 } } @@ -119,7 +119,7 @@ import SwiftUI } static var thumbnailHeight: Double { - thumbnailWidth / 1.7777 + thumbnailWidth / Constants.aspectRatio16x9 } } #endif diff --git a/Shared/Player/VideoPlayerView.swift b/Shared/Player/VideoPlayerView.swift index a08294f2..4c48e199 100644 --- a/Shared/Player/VideoPlayerView.swift +++ b/Shared/Player/VideoPlayerView.swift @@ -19,7 +19,7 @@ struct VideoPlayerView: View { static let hiddenOffset = 0.0 #endif - static let defaultAspectRatio = 16 / 9.0 + static let defaultAspectRatio = Constants.aspectRatio16x9 static var defaultMinimumHeightLeft: Double { #if os(macOS) 335 diff --git a/Shared/Videos/VideoBanner.swift b/Shared/Videos/VideoBanner.swift index db79db72..10a5a0fc 100644 --- a/Shared/Videos/VideoBanner.swift +++ b/Shared/Videos/VideoBanner.swift @@ -219,22 +219,18 @@ struct VideoBanner: View { return watch!.finished ? 0.5 : 1 } - private var thumbnailWidth: Double { - #if os(tvOS) - 356 - #else - 120 - #endif - } - private var thumbnailHeight: Double { #if os(tvOS) 200 #else - 72 + 75 #endif } + private var thumbnailWidth: Double { + thumbnailHeight * Constants.aspectRatio16x9 + } + private var videoDurationLabel: String? { guard videoDuration != 0 else { return nil } return (videoDuration ?? video?.length)?.formattedAsPlaybackTime() diff --git a/Shared/Videos/VideoCell.swift b/Shared/Videos/VideoCell.swift index f3813108..f853c62f 100644 --- a/Shared/Videos/VideoCell.swift +++ b/Shared/Videos/VideoCell.swift @@ -440,7 +440,7 @@ struct VideoCell: View { #endif } .mask(RoundedRectangle(cornerRadius: thumbnailRoundingCornerRadius)) - .modifier(AspectRatioModifier()) + .aspectRatio(Constants.aspectRatio16x9, contentMode: .fill) } private var time: String? { @@ -471,24 +471,6 @@ struct VideoCell: View { .lineLimit(lineLimit) .truncationMode(.middle) } - - struct AspectRatioModifier: ViewModifier { - @Environment(\.horizontalCells) private var horizontalCells - - func body(content: Content) -> some View { - Group { - if horizontalCells { - content - } else { - content - .aspectRatio( - VideoPlayerView.defaultAspectRatio, - contentMode: .fill - ) - } - } - } - } } struct VideoCellThumbnail: View { @@ -496,7 +478,15 @@ struct VideoCellThumbnail: View { @ObservedObject private var thumbnails = ThumbnailsModel.shared var body: some View { - ThumbnailView(url: thumbnails.best(video)) + GeometryReader { geometry in + let (url, quality) = thumbnails.best(video) + let aspectRatio = (quality == .default || quality == .high) ? Constants.aspectRatio4x3 : Constants.aspectRatio16x9 + + ThumbnailView(url: url) + .aspectRatio(aspectRatio, contentMode: .fill) + .frame(width: geometry.size.width, height: geometry.size.height) + .clipped() + } } }