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.
|
/// 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
|
/// Use when a previously cached URL is known to return stale or broken
|
||||||
/// data (e.g. an expired proxied thumbnail URL).
|
/// data (e.g. an expired proxied thumbnail URL).
|
||||||
|
|||||||
@@ -2165,6 +2165,7 @@ struct VideoInfoView: View {
|
|||||||
do {
|
do {
|
||||||
// Use extractURL method - just use the video part
|
// Use extractURL method - just use the video part
|
||||||
let (fullVideo, _, _) = try await contentService.extractURL(originalURL, instance: instance)
|
let (fullVideo, _, _) = try await contentService.extractURL(originalURL, instance: instance)
|
||||||
|
await prefetchNewThumbnailIfNeeded(old: base, new: fullVideo)
|
||||||
invalidateStaleThumbnails(old: base, new: fullVideo)
|
invalidateStaleThumbnails(old: base, new: fullVideo)
|
||||||
loadedVideoDetails[videoID] = fullVideo
|
loadedVideoDetails[videoID] = fullVideo
|
||||||
CachedChannelData.cacheAuthor(fullVideo.author)
|
CachedChannelData.cacheAuthor(fullVideo.author)
|
||||||
@@ -2190,6 +2191,7 @@ struct VideoInfoView: View {
|
|||||||
id: videoID,
|
id: videoID,
|
||||||
instance: instance
|
instance: instance
|
||||||
)
|
)
|
||||||
|
await prefetchNewThumbnailIfNeeded(old: base, new: fullVideo)
|
||||||
invalidateStaleThumbnails(old: base, new: fullVideo)
|
invalidateStaleThumbnails(old: base, new: fullVideo)
|
||||||
loadedVideoDetails[videoID] = fullVideo
|
loadedVideoDetails[videoID] = fullVideo
|
||||||
CachedChannelData.cacheAuthor(fullVideo.author)
|
CachedChannelData.cacheAuthor(fullVideo.author)
|
||||||
@@ -2200,6 +2202,20 @@ struct VideoInfoView: View {
|
|||||||
isLoadingVideoDetails = false
|
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
|
/// Evicts image cache entries for thumbnails whose URLs changed between
|
||||||
/// the queued/list copy of the video and the freshly fetched details, so
|
/// 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
|
/// other views holding the old URLs re-fetch instead of reusing broken
|
||||||
|
|||||||
Reference in New Issue
Block a user