import Foundation import SwiftSignalKit import Postbox import TelegramCore import TelegramUIPreferences import AccountContext import PhotoResources import UniversalMediaPlayer import ChatMessageInteractiveMediaNode private final class PrefetchMediaContext { let fetchDisposable = MetaDisposable() init() { } } struct InChatPrefetchOptions: Equatable { let networkType: MediaAutoDownloadNetworkType let peerType: MediaAutoDownloadPeerType } final class InChatPrefetchManager { private let context: AccountContext private var settings: MediaAutoDownloadSettings private var options: InChatPrefetchOptions? private var messages: [(Message, Media)] = [] private var directionIsToLater: Bool = true private var contexts: [MediaId: PrefetchMediaContext] = [:] init(context: AccountContext) { self.context = context self.settings = context.sharedContext.currentAutomaticMediaDownloadSettings } deinit { for (_, context) in self.contexts { context.fetchDisposable.dispose() } } func updateAutoDownloadSettings(_ settings: MediaAutoDownloadSettings) { if self.settings != settings { self.settings = settings self.update() } } func updateOptions(_ options: InChatPrefetchOptions) { if self.options != options { self.options = options self.update() } } func updateMessages(_ messages: [(Message, Media)], directionIsToLater: Bool) { self.messages = messages self.directionIsToLater = directionIsToLater self.update() } private func update() { guard let options = self.options else { return } var validIds = Set() for (message, media) in self.messages { guard let id = media.id else { continue } if validIds.contains(id) { continue } var mediaResource: MediaResource? var automaticDownload: InteractiveMediaNodeAutodownloadMode = .none if let telegramImage = media as? TelegramMediaImage { mediaResource = largestRepresentationForPhoto(telegramImage)?.resource if shouldDownloadMediaAutomatically(settings: self.settings, peerType: options.peerType, networkType: options.networkType, authorPeerId: nil, contactsPeerIds: [], media: telegramImage) { automaticDownload = .full } } else if let telegramFile = media as? TelegramMediaFile { mediaResource = telegramFile.resource if shouldDownloadMediaAutomatically(settings: self.settings, peerType: options.peerType, networkType: options.networkType, authorPeerId: nil, contactsPeerIds: [], media: telegramFile) { automaticDownload = .full } else if shouldPredownloadMedia(settings: self.settings, peerType: options.peerType, networkType: options.networkType, media: telegramFile) { automaticDownload = .prefetch } } if case .none = automaticDownload { continue } guard let resource = mediaResource else { continue } validIds.insert(id) let context: PrefetchMediaContext if let current = self.contexts[id] { context = current } else { context = PrefetchMediaContext() self.contexts[id] = context let priority: FetchManagerPriority = .foregroundPrefetch(direction: self.directionIsToLater ? .toLater : .toEarlier, localOrder: message.index) if case .full = automaticDownload { if let image = media as? TelegramMediaImage { context.fetchDisposable.set(messageMediaImageInteractiveFetched(fetchManager: self.context.fetchManager, messageId: message.id, messageReference: MessageReference(message), image: image, resource: resource, userInitiated: false, priority: priority, storeToDownloadsPeerId: nil).startStrict()) } else if let _ = media as? TelegramMediaWebFile { //strongSelf.fetchDisposable.set(chatMessageWebFileInteractiveFetched(account: context.account, image: image).startStrict()) } else if let file = media as? TelegramMediaFile { let fetchSignal = messageMediaFileInteractiveFetched(fetchManager: self.context.fetchManager, messageId: message.id, messageReference: MessageReference(message), file: file, userInitiated: false, priority: priority) context.fetchDisposable.set(fetchSignal.startStrict()) } } else if case .prefetch = automaticDownload, message.id.peerId.namespace != Namespaces.Peer.SecretChat { if let file = media as? TelegramMediaFile, let _ = file.size { context.fetchDisposable.set(preloadVideoResource(postbox: self.context.account.postbox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: file), resourceReference: FileMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource), duration: 4.0).startStrict()) } } } } var removeIds: [MediaId] = [] for key in self.contexts.keys { if !validIds.contains(key) { removeIds.append(key) } } for id in removeIds { if let context = self.contexts.removeValue(forKey: id) { context.fetchDisposable.dispose() } } } }