mirror of
https://github.com/yattee/yattee.git
synced 2026-05-12 18:35:05 +00:00
Prefetch fresh video thumbnail before swapping it into info view
This commit is contained in:
@@ -81,6 +81,22 @@ final class ImageLoadingService: Sendable {
|
||||
)
|
||||
}
|
||||
|
||||
/// Warm the image cache for the given URL so a subsequent `LazyImage`
|
||||
/// display hits the cache instantly. Returns after the image is cached
|
||||
/// or the operation fails (silently). Bounded by `timeout` seconds.
|
||||
nonisolated func prefetchImage(for url: URL, timeout: TimeInterval = 3) async {
|
||||
await withTaskGroup(of: Void.self) { group in
|
||||
group.addTask {
|
||||
_ = try? await ImagePipeline.shared.image(for: url)
|
||||
}
|
||||
group.addTask {
|
||||
try? await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
|
||||
}
|
||||
await group.next()
|
||||
group.cancelAll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a specific URL from both the memory and disk image caches.
|
||||
/// Use when a previously cached URL is known to return stale or broken
|
||||
/// data (e.g. an expired proxied thumbnail URL).
|
||||
|
||||
@@ -2165,6 +2165,7 @@ struct VideoInfoView: View {
|
||||
do {
|
||||
// Use extractURL method - just use the video part
|
||||
let (fullVideo, _, _) = try await contentService.extractURL(originalURL, instance: instance)
|
||||
await prefetchNewThumbnailIfNeeded(old: base, new: fullVideo)
|
||||
invalidateStaleThumbnails(old: base, new: fullVideo)
|
||||
loadedVideoDetails[videoID] = fullVideo
|
||||
CachedChannelData.cacheAuthor(fullVideo.author)
|
||||
@@ -2190,6 +2191,7 @@ struct VideoInfoView: View {
|
||||
id: videoID,
|
||||
instance: instance
|
||||
)
|
||||
await prefetchNewThumbnailIfNeeded(old: base, new: fullVideo)
|
||||
invalidateStaleThumbnails(old: base, new: fullVideo)
|
||||
loadedVideoDetails[videoID] = fullVideo
|
||||
CachedChannelData.cacheAuthor(fullVideo.author)
|
||||
@@ -2200,6 +2202,20 @@ struct VideoInfoView: View {
|
||||
isLoadingVideoDetails = false
|
||||
}
|
||||
|
||||
/// Warms the Nuke cache for the freshly fetched thumbnail *before* we
|
||||
/// swap it into the view. Without this, the view observes a URL change,
|
||||
/// flips `LazyImage` to the new URL, and briefly shows a placeholder
|
||||
/// while the download happens — the "thumbnail flash". By the time the
|
||||
/// swap lands, the image is already cached.
|
||||
@MainActor
|
||||
private func prefetchNewThumbnailIfNeeded(old: Video, new: Video) async {
|
||||
guard let newURL = new.bestThumbnail?.url else { return }
|
||||
let oldKey = old.bestThumbnail.map { ImageLoadingService.cacheKey(for: $0.url) }
|
||||
let newKey = ImageLoadingService.cacheKey(for: newURL)
|
||||
guard oldKey != newKey else { return }
|
||||
await ImageLoadingService.shared.prefetchImage(for: newURL)
|
||||
}
|
||||
|
||||
/// Evicts image cache entries for thumbnails whose URLs changed between
|
||||
/// the queued/list copy of the video and the freshly fetched details, so
|
||||
/// other views holding the old URLs re-fetch instead of reusing broken
|
||||
|
||||
Reference in New Issue
Block a user