mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
1096 lines
77 KiB
Swift
1096 lines
77 KiB
Swift
import SGSimpleSettings
|
|
import Foundation
|
|
import UIKit
|
|
import LegacyComponents
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
import Postbox
|
|
import SSignalKit
|
|
import Display
|
|
import TelegramPresentationData
|
|
import DeviceAccess
|
|
import AccountContext
|
|
import ImageCompression
|
|
import MimeTypes
|
|
import LocalMediaResources
|
|
import LegacyUI
|
|
import TextFormat
|
|
|
|
public func guessMimeTypeByFileExtension(_ ext: String) -> String {
|
|
return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary"
|
|
}
|
|
|
|
public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, chatLocation: ChatLocation, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: NSAttributedString, hasSchedule: Bool, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void, presentSchedulePicker: @escaping (Bool, @escaping (Int32) -> Void) -> Void, presentTimerPicker: @escaping (@escaping (Int32) -> Void) -> Void, getCaptionPanelView: @escaping () -> TGCaptionPanelView?) {
|
|
let paintStickersContext = LegacyPaintStickersContext(context: context)
|
|
paintStickersContext.captionPanelView = {
|
|
return getCaptionPanelView()
|
|
}
|
|
|
|
controller.captionsEnabled = captionsEnabled
|
|
controller.inhibitDocumentCaptions = false
|
|
controller.stickersContext = paintStickersContext
|
|
if peer.id != context.account.peerId {
|
|
if peer is TelegramUser {
|
|
controller.hasTimer = hasSchedule
|
|
}
|
|
controller.hasSilentPosting = true
|
|
}
|
|
controller.hasSchedule = hasSchedule
|
|
controller.reminder = peer.id == context.account.peerId
|
|
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
|
controller.hasCoverButton = true
|
|
} else if peer.id == context.account.peerId {
|
|
controller.hasCoverButton = true
|
|
}
|
|
controller.presentScheduleController = { media, done in
|
|
presentSchedulePicker(media, { time in
|
|
done?(time)
|
|
})
|
|
}
|
|
controller.presentTimerController = { done in
|
|
presentTimerPicker { time in
|
|
done?(time)
|
|
}
|
|
}
|
|
controller.selectionLimitExceeded = {
|
|
presentSelectionLimitExceeded()
|
|
}
|
|
controller.localMediaCacheEnabled = false
|
|
controller.shouldStoreAssets = storeCreatedAssets
|
|
controller.shouldShowFileTipIfNeeded = showFileTooltip
|
|
controller.requestSearchController = presentWebSearch
|
|
|
|
if !initialCaption.string.isEmpty {
|
|
controller.editingContext.setForcedCaption(initialCaption)
|
|
}
|
|
}
|
|
|
|
public func legacyAssetPicker(context: AccountContext, presentationData: PresentationData, editingMedia: Bool, fileMode: Bool, peer: Peer?, threadTitle: String?, saveEditedPhotos: Bool, allowGrouping: Bool, selectionLimit: Int) -> Signal<(LegacyComponentsContext) -> TGMediaAssetsController, Void> {
|
|
let isSecretChat = (peer?.id.namespace._internalGetInt32Value() ?? 0) == Namespaces.Peer.SecretChat._internalGetInt32Value()
|
|
|
|
let recipientName: String?
|
|
if let threadTitle {
|
|
recipientName = threadTitle
|
|
} else {
|
|
if peer?.id == context.account.peerId {
|
|
recipientName = presentationData.strings.DialogList_SavedMessages
|
|
} else {
|
|
recipientName = peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
|
}
|
|
}
|
|
|
|
return Signal { subscriber in
|
|
let intent = fileMode ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent
|
|
let defaultVideoPreset = defaultVideoPresetForContext(context)
|
|
UserDefaults.standard.set(defaultVideoPreset.rawValue as NSNumber, forKey: "TG_preferredVideoPreset_v0")
|
|
|
|
DeviceAccess.authorizeAccess(to: .mediaLibrary(.send), presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in
|
|
if !value {
|
|
subscriber.putError(Void())
|
|
return
|
|
}
|
|
|
|
if TGMediaAssetsLibrary.authorizationStatus() == TGMediaLibraryAuthorizationStatusNotDetermined {
|
|
TGMediaAssetsLibrary.requestAuthorization(for: TGMediaAssetAnyType, completion: { (status, group) in
|
|
if !LegacyComponentsGlobals.provider().accessChecker().checkPhotoAuthorizationStatus(for: TGPhotoAccessIntentRead, alertDismissCompletion: nil) {
|
|
subscriber.putError(Void())
|
|
} else {
|
|
Queue.mainQueue().async {
|
|
subscriber.putNext({ context in
|
|
let controller = TGMediaAssetsController(context: context, assetGroup: group, intent: intent, recipientName: recipientName, saveEditedPhotos: !isSecretChat && saveEditedPhotos, allowGrouping: allowGrouping, inhibitSelection: editingMedia, selectionLimit: Int32(selectionLimit))
|
|
return controller!
|
|
})
|
|
subscriber.putCompletion()
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
subscriber.putNext({ context in
|
|
let controller = TGMediaAssetsController(context: context, assetGroup: nil, intent: intent, recipientName: recipientName, saveEditedPhotos: !isSecretChat && saveEditedPhotos, allowGrouping: allowGrouping, inhibitSelection: editingMedia, selectionLimit: Int32(selectionLimit))
|
|
return controller!
|
|
})
|
|
subscriber.putCompletion()
|
|
}
|
|
})
|
|
|
|
return ActionDisposable {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private enum LegacyAssetImageData {
|
|
case image(UIImage)
|
|
case asset(PHAsset)
|
|
case tempFile(String)
|
|
}
|
|
|
|
private enum LegacyAssetVideoData {
|
|
case asset(TGMediaAsset)
|
|
case tempFile(path: String, dimensions: CGSize, duration: Double)
|
|
}
|
|
|
|
private enum LegacyAssetItem {
|
|
case image(data: LegacyAssetImageData, thumbnail: UIImage?, caption: NSAttributedString?, stickers: [FileMediaReference])
|
|
case file(data: LegacyAssetImageData, thumbnail: UIImage?, mimeType: String, name: String, caption: NSAttributedString?)
|
|
case video(data: LegacyAssetVideoData, thumbnail: UIImage?, cover: UIImage?, adjustments: TGVideoEditAdjustments?, caption: NSAttributedString?, asFile: Bool, asAnimation: Bool, stickers: [FileMediaReference])
|
|
}
|
|
|
|
private final class LegacyAssetItemWrapper: NSObject {
|
|
let item: LegacyAssetItem
|
|
let timer: Int?
|
|
let spoiler: Bool?
|
|
let price: Int64?
|
|
let groupedId: Int64?
|
|
let uniqueId: String?
|
|
|
|
init(item: LegacyAssetItem, timer: Int?, spoiler: Bool?, price: Int64?, groupedId: Int64?, uniqueId: String?) {
|
|
self.item = item
|
|
self.timer = timer
|
|
self.spoiler = spoiler
|
|
self.price = price
|
|
self.groupedId = groupedId
|
|
self.uniqueId = uniqueId
|
|
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
public func legacyAssetPickerItemGenerator() -> ((Any?, NSAttributedString?, String?, String?) -> [AnyHashable : Any]?) {
|
|
return { anyDict, caption, hash, uniqueId in
|
|
let dict = anyDict as! NSDictionary
|
|
let stickers = (dict["stickers"] as? [Data])?.compactMap { data -> FileMediaReference? in
|
|
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: data))
|
|
if let file = decoder.decodeRootObject() as? TelegramMediaFile {
|
|
return FileMediaReference.standalone(media: file)
|
|
} else {
|
|
return nil
|
|
}
|
|
} ?? []
|
|
|
|
let price = dict["price"] as? Int64
|
|
|
|
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
|
|
let image = dict["image"] as! UIImage
|
|
let thumbnail = dict["previewImage"] as? UIImage
|
|
let cover = dict["coverImage"] as? UIImage
|
|
|
|
var result: [AnyHashable : Any] = [:]
|
|
if let isAnimation = dict["isAnimation"] as? NSNumber, isAnimation.boolValue {
|
|
let url: String? = (dict["url"] as? String) ?? (dict["url"] as? URL)?.path
|
|
if let url = url {
|
|
let dimensions = image.size
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: 4.0), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: false, asAnimation: true, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
}
|
|
} else {
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), thumbnail: thumbnail, caption: caption, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
}
|
|
return result
|
|
} else if (dict["type"] as! NSString) == "cloudPhoto" {
|
|
let asset = dict["asset"] as! TGMediaAsset
|
|
let thumbnail = dict["previewImage"] as? UIImage
|
|
var asFile = false
|
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
|
asFile = true
|
|
}
|
|
var result: [AnyHashable: Any] = [:]
|
|
if asFile {
|
|
var mimeType = "image/jpeg"
|
|
if let customMimeType = dict["mimeType"] as? String {
|
|
mimeType = customMimeType
|
|
}
|
|
var name = "image.jpg"
|
|
if let customName = dict["fileName"] as? String {
|
|
name = customName
|
|
}
|
|
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .asset(asset.backingAsset), thumbnail: thumbnail, mimeType: mimeType, name: name, caption: caption), timer: nil, spoiler: nil, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
} else {
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .asset(asset.backingAsset), thumbnail: thumbnail, caption: caption, stickers: []), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
}
|
|
return result
|
|
} else if (dict["type"] as! NSString) == "file" {
|
|
if let tempFileUrl = dict["tempFileUrl"] as? URL {
|
|
let thumbnail = dict["previewImage"] as? UIImage
|
|
var mimeType = "application/binary"
|
|
if let customMimeType = dict["mimeType"] as? String {
|
|
mimeType = customMimeType
|
|
}
|
|
var name = "file"
|
|
if let customName = dict["fileName"] as? String {
|
|
name = customName
|
|
}
|
|
|
|
if let isAnimation = dict["isAnimation"] as? NSNumber, isAnimation.boolValue, mimeType == "video/mp4" {
|
|
var result: [AnyHashable: Any] = [:]
|
|
|
|
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
|
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: tempFileUrl.path, dimensions: dimensions, duration: duration), thumbnail: thumbnail, cover: nil, adjustments: nil, caption: caption, asFile: false, asAnimation: true, stickers: []), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
return result
|
|
}
|
|
|
|
var result: [AnyHashable: Any] = [:]
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .tempFile(tempFileUrl.path), thumbnail: thumbnail, mimeType: mimeType, name: name, caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
return result
|
|
}
|
|
} else if (dict["type"] as! NSString) == "video" {
|
|
let thumbnail = dict["previewImage"] as? UIImage
|
|
let cover = dict["coverImage"] as? UIImage
|
|
var asFile = false
|
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
|
asFile = true
|
|
}
|
|
|
|
if let asset = dict["asset"] as? TGMediaAsset {
|
|
var result: [AnyHashable: Any] = [:]
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .asset(asset), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
return result
|
|
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
|
|
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
|
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
|
var result: [AnyHashable: Any] = [:]
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
return result
|
|
}
|
|
} else if (dict["type"] as! NSString) == "cameraVideo" {
|
|
let thumbnail = dict["previewImage"] as? UIImage
|
|
let cover = dict["coverImage"] as? UIImage
|
|
var asFile = false
|
|
if let document = dict["document"] as? NSNumber, document.boolValue {
|
|
asFile = true
|
|
}
|
|
|
|
let url: String? = (dict["url"] as? String) ?? (dict["url"] as? URL)?.path
|
|
|
|
if let url = url, let previewImage = dict["previewImage"] as? UIImage {
|
|
let dimensions = previewImage.pixelSize()
|
|
let duration = (dict["duration"]! as AnyObject).doubleValue!
|
|
var result: [AnyHashable: Any] = [:]
|
|
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), thumbnail: thumbnail, cover: cover, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, spoiler: (dict["spoiler"] as? NSNumber)?.boolValue, price: price, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value, uniqueId: uniqueId)
|
|
return result
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public func legacyEnqueueGifMessage(account: Account, data: Data, correlationId: Int64? = nil) -> Signal<EnqueueMessage, Void> {
|
|
return Signal { subscriber in
|
|
if let previewImage = UIImage(data: data) {
|
|
let dimensions = previewImage.size
|
|
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
|
|
|
let thumbnailSize = dimensions.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
|
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
|
|
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
|
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let tempFilePath = NSTemporaryDirectory() + "\(randomId).gif"
|
|
|
|
let _ = try? FileManager.default.removeItem(atPath: tempFilePath)
|
|
let _ = try? data.write(to: URL(fileURLWithPath: tempFilePath), options: [.atomic])
|
|
|
|
let resource = LocalFileGifMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: tempFilePath)
|
|
let fileName: String = "video.mp4"
|
|
|
|
let finalDimensions = TGMediaVideoConverter.dimensions(for: dimensions, adjustments: nil, preset: TGMediaVideoConversionPresetAnimation)
|
|
|
|
var fileAttributes: [TelegramMediaFileAttribute] = []
|
|
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil, videoCodec: nil))
|
|
fileAttributes.append(.FileName(fileName: fileName))
|
|
fileAttributes.append(.Animated)
|
|
|
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
|
subscriber.putNext(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []))
|
|
subscriber.putCompletion()
|
|
} else {
|
|
subscriber.putError(Void())
|
|
}
|
|
|
|
return EmptyDisposable
|
|
} |> runOn(Queue.concurrentDefaultQueue())
|
|
}
|
|
|
|
public func legacyEnqueueVideoMessage(account: Account, data: Data, correlationId: Int64? = nil) -> Signal<EnqueueMessage, Void> {
|
|
return Signal { subscriber in
|
|
if let previewImage = UIImage(data: data) {
|
|
let dimensions = previewImage.size
|
|
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
|
|
|
let thumbnailSize = dimensions.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
|
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
|
|
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
|
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let tempFilePath = NSTemporaryDirectory() + "\(randomId).mp4"
|
|
|
|
let _ = try? FileManager.default.removeItem(atPath: tempFilePath)
|
|
let _ = try? data.write(to: URL(fileURLWithPath: tempFilePath), options: [.atomic])
|
|
|
|
let resource = LocalFileGifMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: tempFilePath)
|
|
let fileName: String = "video.mp4"
|
|
|
|
let finalDimensions = TGMediaVideoConverter.dimensions(for: dimensions, adjustments: nil, preset: TGMediaVideoConversionPresetAnimation)
|
|
|
|
var fileAttributes: [TelegramMediaFileAttribute] = []
|
|
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil, videoCodec: nil))
|
|
fileAttributes.append(.FileName(fileName: fileName))
|
|
fileAttributes.append(.Animated)
|
|
|
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
|
subscriber.putNext(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: []))
|
|
subscriber.putCompletion()
|
|
} else {
|
|
subscriber.putError(Void())
|
|
}
|
|
|
|
return EmptyDisposable
|
|
} |> runOn(Queue.concurrentDefaultQueue())
|
|
}
|
|
|
|
public struct LegacyAssetPickerEnqueueMessage {
|
|
public var message: EnqueueMessage
|
|
public var uniqueId: String?
|
|
public var isFile: Bool
|
|
}
|
|
|
|
public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any]) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> {
|
|
return Signal { subscriber in
|
|
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
|
|
var messages: [LegacyAssetPickerEnqueueMessage] = []
|
|
|
|
struct EnqueuePaidMessage {
|
|
var price: Int64
|
|
var text: String
|
|
var entities: [MessageTextEntity]
|
|
var media: [Media]
|
|
}
|
|
|
|
var paidMessage: EnqueuePaidMessage?
|
|
|
|
outer: for item in (anyValues as! NSArray) {
|
|
if let item = (item as? NSDictionary)?.object(forKey: "item") as? LegacyAssetItemWrapper {
|
|
switch item.item {
|
|
case let .image(data, thumbnail, caption, stickers):
|
|
var representations: [TelegramMediaImageRepresentation] = []
|
|
if let thumbnail = thumbnail {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
let thumbnailSize = thumbnail.size.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
|
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
|
|
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
}
|
|
switch data {
|
|
case let .image(image):
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpeg"
|
|
let scaledSize = image.size.aspectFittedOrSmaller(CGSize(width: 1280.0, height: 1280.0))
|
|
if let scaledImage = TGScaleImageToPixelSize(image, scaledSize) {
|
|
let tempFile = TempBox.shared.tempFile(fileName: "file")
|
|
defer {
|
|
TempBox.shared.dispose(tempFile)
|
|
}
|
|
// MARK: Swiftgram
|
|
if let scaledImageData = compressImageToJPEG(scaledImage, quality: Float(SGSimpleSettings.shared.outgoingPhotoQuality) / 100.0, tempFilePath: tempFile.path) {
|
|
let _ = try? scaledImageData.write(to: URL(fileURLWithPath: tempFilePath))
|
|
|
|
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
|
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
|
|
var imageFlags: TelegramMediaImageFlags = []
|
|
|
|
var attributes: [MessageAttribute] = []
|
|
|
|
var stickerFiles: [TelegramMediaFile] = []
|
|
if !stickers.isEmpty {
|
|
for fileReference in stickers {
|
|
stickerFiles.append(fileReference.media)
|
|
}
|
|
}
|
|
if !stickerFiles.isEmpty {
|
|
attributes.append(EmbeddedMediaStickersMessageAttribute(files: stickerFiles))
|
|
imageFlags.insert(.hasStickers)
|
|
}
|
|
|
|
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: imageFlags)
|
|
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
|
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
|
}
|
|
if let spoiler = item.spoiler, spoiler {
|
|
attributes.append(MediaSpoilerMessageAttribute())
|
|
}
|
|
|
|
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
|
if !entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
|
}
|
|
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
|
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
|
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
|
if let file = value.file {
|
|
if let packId = value.interactivelySelectedFromPackId {
|
|
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
|
}
|
|
}
|
|
}
|
|
})
|
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
|
for entity in entities {
|
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
|
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
|
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
|
bubbleUpEmojiOrStickersets.append(packId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let price = item.price {
|
|
if var current = paidMessage {
|
|
if current.text.isEmpty && !text.string.isEmpty {
|
|
current.text = text.string
|
|
current.entities = entities
|
|
}
|
|
current.media.append(media)
|
|
paidMessage = current
|
|
} else {
|
|
paidMessage = EnqueuePaidMessage(
|
|
price: price,
|
|
text: text.string,
|
|
entities: entities,
|
|
media: [media]
|
|
)
|
|
}
|
|
} else {
|
|
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: false))
|
|
}
|
|
}
|
|
}
|
|
case let .asset(asset):
|
|
if context.sharedContext.immediateExperimentalUISettings.storiesJpegExperiment {
|
|
let sizes: [Int32] = [2048, 1280]
|
|
let formats: [MediaImageFormat] = [.jxl, .jpeg]
|
|
let qualities: [Int32: [Int32]] = [
|
|
MediaImageFormat.jxl.rawValue: [
|
|
50,
|
|
75
|
|
],
|
|
MediaImageFormat.jpeg.rawValue: [
|
|
75
|
|
]
|
|
]
|
|
for sizeSide in sizes {
|
|
for format in formats {
|
|
for quality in qualities[format.rawValue]! {
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let resource = PhotoLibraryMediaResource(
|
|
localIdentifier: asset.localIdentifier,
|
|
uniqueId: Int64.random(in: Int64.min ... Int64.max),
|
|
width: sizeSide,
|
|
height: sizeSide,
|
|
format: format,
|
|
quality: quality
|
|
)
|
|
|
|
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
|
let scaledSize = size.aspectFittedOrSmaller(CGSize(width: CGFloat(sizeSide), height: CGFloat(sizeSide)))
|
|
|
|
let media: Media
|
|
media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: format == .jxl ? "image/jxl" : "image/jpeg", size: nil, attributes: [
|
|
.FileName(fileName: format == .jxl ? "image\(sizeSide)-q\(quality).jxl" : "image\(sizeSide)-q\(quality).jpg"),
|
|
.ImageSize(size: PixelDimensions(scaledSize))
|
|
], alternativeRepresentations: [])
|
|
|
|
var attributes: [MessageAttribute] = []
|
|
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
|
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
|
}
|
|
if let spoiler = item.spoiler, spoiler {
|
|
attributes.append(MediaSpoilerMessageAttribute())
|
|
}
|
|
|
|
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
|
if !entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
|
}
|
|
|
|
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
|
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
|
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
|
if let file = value.file {
|
|
if let packId = value.interactivelySelectedFromPackId {
|
|
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
|
}
|
|
}
|
|
}
|
|
})
|
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
|
for entity in entities {
|
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
|
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
|
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
|
bubbleUpEmojiOrStickersets.append(packId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let price = item.price {
|
|
if var current = paidMessage {
|
|
if current.text.isEmpty && !text.string.isEmpty {
|
|
current.text = text.string
|
|
current.entities = entities
|
|
}
|
|
current.media.append(media)
|
|
paidMessage = current
|
|
} else {
|
|
paidMessage = EnqueuePaidMessage(
|
|
price: price,
|
|
text: text.string,
|
|
entities: entities,
|
|
media: [media]
|
|
)
|
|
}
|
|
} else {
|
|
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: false))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
|
let scaledSize = size.aspectFittedOrSmaller(CGSize(width: 1280.0, height: 1280.0))
|
|
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: Int64.random(in: Int64.min ... Int64.max))
|
|
|
|
let media: Media
|
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
|
|
|
var attributes: [MessageAttribute] = []
|
|
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
|
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
|
}
|
|
if let spoiler = item.spoiler, spoiler {
|
|
attributes.append(MediaSpoilerMessageAttribute())
|
|
}
|
|
|
|
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
|
if !entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
|
}
|
|
|
|
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
|
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
|
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
|
if let file = value.file {
|
|
if let packId = value.interactivelySelectedFromPackId {
|
|
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
|
}
|
|
}
|
|
}
|
|
})
|
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
|
for entity in entities {
|
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
|
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
|
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
|
bubbleUpEmojiOrStickersets.append(packId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let price = item.price {
|
|
if var current = paidMessage {
|
|
if current.text.isEmpty && !text.string.isEmpty {
|
|
current.text = text.string
|
|
current.entities = entities
|
|
}
|
|
current.media.append(media)
|
|
paidMessage = current
|
|
} else {
|
|
paidMessage = EnqueuePaidMessage(
|
|
price: price,
|
|
text: text.string,
|
|
entities: entities,
|
|
media: [media]
|
|
)
|
|
}
|
|
} else {
|
|
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: false))
|
|
}
|
|
}
|
|
case .tempFile:
|
|
break
|
|
}
|
|
case let .file(data, thumbnail, mimeType, name, caption):
|
|
switch data {
|
|
case let .tempFile(path):
|
|
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
|
if let thumbnail = thumbnail {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
let thumbnailSize = thumbnail.size.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
|
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
|
|
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
|
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
}
|
|
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId)
|
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: fileSize(path), attributes: [.FileName(fileName: name)], alternativeRepresentations: [])
|
|
|
|
var attributes: [MessageAttribute] = []
|
|
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
|
if !entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
|
}
|
|
|
|
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
|
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
|
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
|
if let file = value.file {
|
|
if let packId = value.interactivelySelectedFromPackId {
|
|
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
|
}
|
|
}
|
|
}
|
|
})
|
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
|
for entity in entities {
|
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
|
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
|
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
|
bubbleUpEmojiOrStickersets.append(packId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let price = item.price {
|
|
if var current = paidMessage {
|
|
if current.text.isEmpty && !text.string.isEmpty {
|
|
current.text = text.string
|
|
current.entities = entities
|
|
}
|
|
current.media.append(media)
|
|
paidMessage = current
|
|
} else {
|
|
paidMessage = EnqueuePaidMessage(
|
|
price: price,
|
|
text: text.string,
|
|
entities: entities,
|
|
media: [media]
|
|
)
|
|
}
|
|
} else {
|
|
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: true))
|
|
}
|
|
case let .asset(asset):
|
|
var randomId: Int64 = 0
|
|
arc4random_buf(&randomId, 8)
|
|
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: Int64.random(in: Int64.min ... Int64.max))
|
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)], alternativeRepresentations: [])
|
|
|
|
var attributes: [MessageAttribute] = []
|
|
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
|
if !entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
|
}
|
|
|
|
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
|
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
|
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
|
if let file = value.file {
|
|
if let packId = value.interactivelySelectedFromPackId {
|
|
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
|
}
|
|
}
|
|
}
|
|
})
|
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
|
for entity in entities {
|
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
|
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
|
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
|
bubbleUpEmojiOrStickersets.append(packId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let price = item.price {
|
|
if var current = paidMessage {
|
|
if current.text.isEmpty && !text.string.isEmpty {
|
|
current.text = text.string
|
|
current.entities = entities
|
|
}
|
|
current.media.append(media)
|
|
paidMessage = current
|
|
} else {
|
|
paidMessage = EnqueuePaidMessage(
|
|
price: price,
|
|
text: text.string,
|
|
entities: entities,
|
|
media: [media]
|
|
)
|
|
}
|
|
} else {
|
|
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: true))
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
case let .video(data, thumbnail, cover, adjustments, caption, asFile, asAnimation, stickers):
|
|
var adjustments = adjustments
|
|
var finalDimensions: CGSize
|
|
var finalDuration: Double
|
|
switch data {
|
|
case let .asset(asset):
|
|
if let adjustments = adjustments {
|
|
if adjustments.cropApplied(forAvatar: false) {
|
|
finalDimensions = adjustments.cropRect.size
|
|
if adjustments.cropOrientation == .left || adjustments.cropOrientation == .right {
|
|
finalDimensions = CGSize(width: finalDimensions.height, height: finalDimensions.width)
|
|
}
|
|
} else {
|
|
finalDimensions = asset.dimensions
|
|
}
|
|
if adjustments.trimEndValue > 0.0 {
|
|
finalDuration = adjustments.trimEndValue - adjustments.trimStartValue
|
|
} else {
|
|
finalDuration = asset.videoDuration
|
|
}
|
|
} else {
|
|
finalDimensions = asset.dimensions
|
|
finalDuration = asset.videoDuration
|
|
}
|
|
case let .tempFile(_, dimensions, duration):
|
|
finalDimensions = dimensions
|
|
finalDuration = duration
|
|
}
|
|
|
|
/*if !asAnimation {
|
|
finalDimensions = TGFitSize(finalDimensions, CGSize(width: 848.0, height: 848.0))
|
|
}*/
|
|
|
|
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
|
if let thumbnail = thumbnail {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
|
let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)!
|
|
if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) {
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
|
|
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
|
}
|
|
}
|
|
|
|
var videoCover: TelegramMediaImage?
|
|
if let cover {
|
|
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
|
let coverSize = cover.size.aspectFitted(CGSize(width: 320.0, height: 320.0))
|
|
let coverImage = TGScaleImageToPixelSize(cover, coverSize)!
|
|
if let coverData = coverImage.jpegData(compressionQuality: 0.6) {
|
|
account.postbox.mediaBox.storeResourceData(resource.id, data: coverData)
|
|
videoCover = TelegramMediaImage(
|
|
imageId: MediaId(namespace: 0, id: 0),
|
|
representations: [
|
|
TelegramMediaImageRepresentation(dimensions: PixelDimensions(coverSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)
|
|
],
|
|
immediateThumbnailData: nil,
|
|
reference: nil,
|
|
partialReference: nil,
|
|
flags: []
|
|
)
|
|
}
|
|
}
|
|
|
|
let defaultPreset = TGMediaVideoConversionPreset(rawValue: UInt32(UserDefaults.standard.integer(forKey: "TG_preferredVideoPreset_v0")))
|
|
|
|
var preset: TGMediaVideoConversionPreset = TGMediaVideoConversionPresetCompressedMedium
|
|
if let selectedPreset = adjustments?.preset {
|
|
preset = selectedPreset
|
|
} else if preset == TGMediaVideoConversionPresetCompressedDefault && defaultPreset != TGMediaVideoConversionPresetCompressedDefault {
|
|
preset = defaultPreset
|
|
}
|
|
if asAnimation {
|
|
preset = TGMediaVideoConversionPresetAnimation
|
|
}
|
|
|
|
// MARK: Swiftgram
|
|
// TODO(swiftgram): Nice thumbnail
|
|
var asTelescope = false
|
|
if let strongAdjustments = adjustments, strongAdjustments.sendAsTelescope {
|
|
asTelescope = true
|
|
// Final size
|
|
let size = CGSize(width: finalDimensions.width, height: finalDimensions.height)
|
|
|
|
// Respecting user's crop
|
|
var cropRect = strongAdjustments.cropRect
|
|
|
|
let originalSize: CGSize
|
|
if strongAdjustments.cropApplied(forAvatar: false) {
|
|
originalSize = strongAdjustments.originalSize
|
|
} else {
|
|
// It's a hack, video is resized according to the quality preset
|
|
// To prevent this resize we must set original size the same as the after-resized video
|
|
originalSize = size
|
|
}
|
|
|
|
// Already square
|
|
if abs(finalDimensions.width - finalDimensions.height) < CGFloat.ulpOfOne {
|
|
cropRect = cropRect.insetBy(dx: 13.0, dy: 13.0)
|
|
cropRect = cropRect.offsetBy(dx: 2.0, dy: 3.0)
|
|
} else {
|
|
// Need to make a square
|
|
let shortestSide = min(size.width, size.height)
|
|
let newX = cropRect.origin.x + (size.width - shortestSide) / 2.0
|
|
let newY = cropRect.origin.y + (size.height - shortestSide) / 2.0
|
|
cropRect = CGRect(x: newX, y: newY, width: shortestSide, height: shortestSide)
|
|
print("size.width \(size.width)")
|
|
print("size.height \(size.height)")
|
|
print("shortestSide \(shortestSide)")
|
|
print("cropRect.origin.x \(cropRect.origin.x)")
|
|
print("cropRect.origin.y \(cropRect.origin.y)")
|
|
print("newX \(newX)")
|
|
print("newY \(newY)")
|
|
}
|
|
|
|
let maxDuration: Double = 60.0
|
|
let trimmedDuration: TimeInterval
|
|
if strongAdjustments.trimApplied() {
|
|
trimmedDuration = strongAdjustments.trimEndValue - strongAdjustments.trimStartValue
|
|
} else {
|
|
trimmedDuration = finalDuration
|
|
}
|
|
|
|
let trimEndValueLimited: TimeInterval
|
|
if trimmedDuration > maxDuration {
|
|
trimEndValueLimited = strongAdjustments.trimEndValue - (trimmedDuration - maxDuration)
|
|
} else {
|
|
trimEndValueLimited = strongAdjustments.trimEndValue
|
|
}
|
|
|
|
print("Preset TGMediaVideoConversionPresetVideoMessageHD \(TGMediaVideoConversionPresetVideoMessageHD)")
|
|
print("Preset TGMediaVideoConversionPresetVideoMessage \(TGMediaVideoConversionPresetVideoMessage)")
|
|
print("Preset TGMediaVideoConversionPresetCompressedLow \(TGMediaVideoConversionPresetCompressedLow)")
|
|
|
|
// Dynamically calculate size with different presets and use the best one
|
|
for presetTest in [TGMediaVideoConversionPresetVideoMessageHD, TGMediaVideoConversionPresetVideoMessage, TGMediaVideoConversionPresetCompressedLow] {
|
|
adjustments = TGVideoEditAdjustments(originalSize: originalSize, cropRect: cropRect, cropOrientation: strongAdjustments.cropOrientation, cropRotation: strongAdjustments.cropRotation, cropLockedAspectRatio: 1.0, cropMirrored: strongAdjustments.cropMirrored, trimStartValue: strongAdjustments.trimStartValue, trimEndValue: trimEndValueLimited, toolValues: strongAdjustments.toolValues, paintingData: strongAdjustments.paintingData, sendAsGif: false, sendAsTelescope: strongAdjustments.sendAsTelescope, preset: presetTest)
|
|
|
|
finalDimensions = TGMediaVideoConverter.dimensions(for: finalDimensions, adjustments: adjustments, preset: presetTest)
|
|
|
|
let estimatedVideoMessageSize = TGMediaVideoConverter.estimatedSize(for: presetTest, duration: finalDuration, hasAudio: true)
|
|
if estimatedVideoMessageSize < 8 * 1024 * 1024 {
|
|
print("Using preset \(presetTest)")
|
|
preset = presetTest
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
var resourceAdjustments: VideoMediaResourceAdjustments?
|
|
if let adjustments = adjustments {
|
|
if adjustments.trimApplied() {
|
|
finalDuration = adjustments.trimEndValue - adjustments.trimStartValue
|
|
}
|
|
|
|
if let dict = adjustments.dictionary(), let data = try? NSKeyedArchiver.archivedData(withRootObject: dict, requiringSecureCoding: false) {
|
|
let adjustmentsData = MemoryBuffer(data: data)
|
|
let digest = MemoryBuffer(data: adjustmentsData.md5Digest())
|
|
resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest, isStory: false)
|
|
}
|
|
}
|
|
|
|
let resource: TelegramMediaResource
|
|
var fileName: String = "video.mp4"
|
|
switch data {
|
|
case let .asset(asset):
|
|
if let assetFileName = asset.fileName, !assetFileName.isEmpty {
|
|
fileName = (assetFileName as NSString).lastPathComponent
|
|
}
|
|
resource = VideoLibraryMediaResource(localIdentifier: asset.backingAsset.localIdentifier, conversion: asFile ? .passthrough : .compress(resourceAdjustments))
|
|
case let .tempFile(path, _, _):
|
|
if asFile || (asAnimation && !path.contains(".jpg")) {
|
|
if let size = fileSize(path) {
|
|
resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: size)
|
|
account.postbox.mediaBox.moveResourceData(resource.id, fromTempPath: path)
|
|
} else {
|
|
continue outer
|
|
}
|
|
} else {
|
|
resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: path, adjustments: resourceAdjustments)
|
|
}
|
|
}
|
|
|
|
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: !asAnimation)
|
|
|
|
var fileAttributes: [TelegramMediaFileAttribute] = []
|
|
fileAttributes.append(.FileName(fileName: fileName))
|
|
if asAnimation {
|
|
fileAttributes.append(.Animated)
|
|
}
|
|
if !asFile {
|
|
fileAttributes.append(.Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil, videoCodec: nil))
|
|
if let adjustments = adjustments {
|
|
if adjustments.sendAsGif {
|
|
fileAttributes.append(.Animated)
|
|
}
|
|
}
|
|
|
|
if estimatedSize > 10 * 1024 * 1024 {
|
|
fileAttributes.append(.hintFileIsLarge)
|
|
}
|
|
} else {
|
|
if case let .asset(asset) = data, let phAsset = asset.backingAsset, let resource = PHAssetResource.assetResources(for: phAsset).last, let sizeValue = resource.value(forKey: "fileSize") as? CLong, sizeValue > 0 {
|
|
let size = Int64(bitPattern: UInt64(sizeValue))
|
|
|
|
if size > 10 * 1024 * 1024 {
|
|
fileAttributes.append(.hintFileIsLarge)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var attributes: [MessageAttribute] = []
|
|
|
|
var stickerFiles: [TelegramMediaFile] = []
|
|
if !stickers.isEmpty {
|
|
for fileReference in stickers {
|
|
stickerFiles.append(fileReference.media)
|
|
}
|
|
}
|
|
if !stickerFiles.isEmpty {
|
|
attributes.append(EmbeddedMediaStickersMessageAttribute(files: stickerFiles))
|
|
fileAttributes.append(.HasLinkedStickers)
|
|
}
|
|
// MARK: Swiftgram
|
|
if asTelescope {
|
|
fileAttributes = [.FileName(fileName: "video.mp4"), .Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo], preloadSize: nil, coverTime: nil, videoCodec: nil)]
|
|
}
|
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], videoCover: videoCover, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: [])
|
|
|
|
if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) {
|
|
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
|
}
|
|
if let spoiler = item.spoiler, spoiler {
|
|
attributes.append(MediaSpoilerMessageAttribute())
|
|
}
|
|
|
|
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
|
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
|
if !entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
|
}
|
|
|
|
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
|
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
|
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
|
if let file = value.file {
|
|
if let packId = value.interactivelySelectedFromPackId {
|
|
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
|
}
|
|
}
|
|
}
|
|
})
|
|
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
|
for entity in entities {
|
|
if case let .CustomEmoji(_, fileId) = entity.type {
|
|
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
|
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
|
bubbleUpEmojiOrStickersets.append(packId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let price = item.price {
|
|
if var current = paidMessage {
|
|
if current.text.isEmpty && !text.string.isEmpty {
|
|
current.text = text.string
|
|
current.entities = entities
|
|
}
|
|
current.media.append(media)
|
|
paidMessage = current
|
|
} else {
|
|
paidMessage = EnqueuePaidMessage(
|
|
price: price,
|
|
text: text.string,
|
|
entities: entities,
|
|
media: [media]
|
|
)
|
|
}
|
|
} else {
|
|
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: asFile))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let paidMessage {
|
|
var attributes: [MessageAttribute] = []
|
|
if !paidMessage.entities.isEmpty {
|
|
attributes.append(TextEntitiesMessageAttribute(entities: paidMessage.entities))
|
|
}
|
|
messages.append(
|
|
LegacyAssetPickerEnqueueMessage(
|
|
message: .message(
|
|
text: paidMessage.text,
|
|
attributes: attributes,
|
|
inlineStickers: [:],
|
|
mediaReference: .standalone(
|
|
media: TelegramMediaPaidContent(
|
|
amount: paidMessage.price,
|
|
extendedMedia: paidMessage.media.map { .full(media: $0) }
|
|
)),
|
|
threadId: nil,
|
|
replyToMessageId: nil,
|
|
replyToStoryId: nil,
|
|
localGroupingKey: nil,
|
|
correlationId: nil,
|
|
bubbleUpEmojiOrStickersets: []
|
|
),
|
|
uniqueId: nil,
|
|
isFile: false
|
|
)
|
|
)
|
|
}
|
|
|
|
subscriber.putNext(messages)
|
|
subscriber.putCompletion()
|
|
}, error: { _ in
|
|
subscriber.putError(Void())
|
|
}, completed: nil)
|
|
|
|
return ActionDisposable {
|
|
disposable?.dispose()
|
|
}
|
|
}
|
|
}
|