jellyflood/Shared/Objects/MediaPlayerManager/PreviewImageProvider/ChapterPreviewImageProvider...

71 lines
2.1 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//
import Factory
import Get
import JellyfinAPI
import UIKit
// TODO: preload chapter images
// - somehow tell player if there are no images
// and don't present popup overlay
// TODO: just use Nuke image pipeline
class ChapterPreviewImageProvider: PreviewImageProvider {
let chapters: [ChapterInfo.FullInfo]
@MainActor
private var images: [Int: UIImage] = [:]
@MainActor
private var imageTasks: [Int: Task<UIImage?, Never>] = [:]
init(chapters: [ChapterInfo.FullInfo]) {
self.chapters = chapters
}
func imageIndex(for seconds: Duration) -> Int? {
guard let currentChapterIndex = chapters
.firstIndex(where: {
guard let startSeconds = $0.chapterInfo.startSeconds else { return false }
return startSeconds > seconds
}
) else { return nil }
return max(0, currentChapterIndex - 1)
}
@MainActor
func image(for seconds: Duration) async -> UIImage? {
guard let chapterIndex = imageIndex(for: seconds) else { return nil }
if let image = images[chapterIndex] {
return image
}
if let task = imageTasks[chapterIndex] {
return await task.value
}
let newTask = Task<UIImage?, Never> {
let client = Container.shared.currentUserSession()!.client
guard let chapterInfo = chapters[safe: chapterIndex], let imageUrl = chapterInfo.imageSource.url else { return nil }
let request: Request<Data> = .init(url: imageUrl)
guard let response = try? await client.send(request) else { return nil }
guard let image = UIImage(data: response.value) else { return nil }
return image
}
imageTasks[chapterIndex] = newTask
return await newTask.value
}
}