import Foundation #if os(macOS) import PostboxMac #else import Postbox #endif private let typeFileName: Int32 = 0 private let typeSticker: Int32 = 1 private let typeImageSize: Int32 = 2 private let typeAnimated: Int32 = 3 private let typeVideo: Int32 = 4 private let typeAudio: Int32 = 5 private let typeHasLinkedStickers: Int32 = 6 public enum TelegramMediaFileAttribute: Coding { case FileName(fileName: String) case Sticker(displayText: String) case ImageSize(size: CGSize) case Animated case Video(duration: Int, size: CGSize) case Audio(isVoice: Bool, duration: Int, title: String?, performer: String?, waveform: MemoryBuffer?) case HasLinkedStickers public init(decoder: Decoder) { let type: Int32 = decoder.decodeInt32ForKey("t") switch type { case typeFileName: self = .FileName(fileName: decoder.decodeStringForKey("fn")) case typeSticker: self = .Sticker(displayText: decoder.decodeStringForKey("dt")) case typeImageSize: self = .ImageSize(size: CGSize(width: CGFloat(decoder.decodeInt32ForKey("w")), height: CGFloat(decoder.decodeInt32ForKey("h")))) case typeAnimated: self = .Animated case typeVideo: self = .Video(duration: Int(decoder.decodeInt32ForKey("du")), size: CGSize(width: CGFloat(decoder.decodeInt32ForKey("w")), height: CGFloat(decoder.decodeInt32ForKey("h")))) case typeAudio: let waveformBuffer = decoder.decodeBytesForKeyNoCopy("wf") var waveform: MemoryBuffer? if let waveformBuffer = waveformBuffer { waveform = MemoryBuffer(copyOf: waveformBuffer) } self = .Audio(isVoice: decoder.decodeInt32ForKey("iv") != 0, duration: Int(decoder.decodeInt32ForKey("du")), title: decoder.decodeStringForKey("ti"), performer: decoder.decodeStringForKey("pe"), waveform: waveform) case typeHasLinkedStickers: self = .HasLinkedStickers default: preconditionFailure() } } public func encode(_ encoder: Encoder) { switch self { case let .FileName(fileName): encoder.encodeInt32(typeFileName, forKey: "t") encoder.encodeString(fileName, forKey: "fn") case let .Sticker(displayText): encoder.encodeInt32(typeSticker, forKey: "t") encoder.encodeString(displayText, forKey: "dt") case let .ImageSize(size): encoder.encodeInt32(typeImageSize, forKey: "t") encoder.encodeInt32(Int32(size.width), forKey: "w") encoder.encodeInt32(Int32(size.height), forKey: "h") case .Animated: encoder.encodeInt32(typeAnimated, forKey: "t") case let .Video(duration, size): encoder.encodeInt32(typeVideo, forKey: "t") encoder.encodeInt32(Int32(duration), forKey: "du") encoder.encodeInt32(Int32(size.width), forKey: "w") encoder.encodeInt32(Int32(size.height), forKey: "h") case let .Audio(isVoice, duration, title, performer, waveform): encoder.encodeInt32(typeAudio, forKey: "t") encoder.encodeInt32(isVoice ? 1 : 0, forKey: "iv") encoder.encodeInt32(Int32(duration), forKey: "du") if let title = title { encoder.encodeString(title, forKey: "ti") } if let performer = performer { encoder.encodeString(performer, forKey: "pe") } if let waveform = waveform { encoder.encodeBytes(waveform, forKey: "wf") } case .HasLinkedStickers: encoder.encodeInt32(typeHasLinkedStickers, forKey: "t") } } } public final class TelegramMediaFile: Media, Equatable { public let fileId: MediaId public let resource: TelegramMediaResource public let previewRepresentations: [TelegramMediaImageRepresentation] public let mimeType: String public let size: Int? public let attributes: [TelegramMediaFileAttribute] public let peerIds: [PeerId] = [] public var id: MediaId? { return self.fileId } public init(fileId: MediaId, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], mimeType: String, size: Int?, attributes: [TelegramMediaFileAttribute]) { self.fileId = fileId self.resource = resource self.previewRepresentations = previewRepresentations self.mimeType = mimeType self.size = size self.attributes = attributes } public init(decoder: Decoder) { self.fileId = MediaId(decoder.decodeBytesForKeyNoCopy("i")!) self.resource = decoder.decodeObjectForKey("r") as! TelegramMediaResource self.previewRepresentations = decoder.decodeObjectArrayForKey("pr") self.mimeType = decoder.decodeStringForKey("mt") if let size = (decoder.decodeInt32ForKey("s") as Int32?) { self.size = Int(size) } else { self.size = nil } self.attributes = decoder.decodeObjectArrayForKey("at") } public func encode(_ encoder: Encoder) { let buffer = WriteBuffer() self.fileId.encodeToBuffer(buffer) encoder.encodeBytes(buffer, forKey: "i") encoder.encodeObject(self.resource, forKey: "r") encoder.encodeObjectArray(self.previewRepresentations, forKey: "pr") encoder.encodeString(self.mimeType, forKey: "mt") if let size = self.size { encoder.encodeInt32(Int32(size), forKey: "s") } else { encoder.encodeNil(forKey: "s") } encoder.encodeObjectArray(self.attributes, forKey: "at") } public var fileName: String? { get { for attribute in self.attributes { switch attribute { case let .FileName(fileName): return fileName case _: break } } return nil } } public var isSticker: Bool { for attribute in self.attributes { if case .Sticker = attribute { return true } } return false } public var isVideo: Bool { for attribute in self.attributes { if case .Video = attribute { return true } } return false } public var isAnimated: Bool { for attribute in self.attributes { if case .Animated = attribute { return true } } return false } public var isMusic: Bool { for attribute in self.attributes { if case .Audio(false, _, _, _, _) = attribute { return true } } return false } public var isVoice: Bool { for attribute in self.attributes { if case .Audio(true, _, _, _, _) = attribute { return true } } return false } public var dimensions: CGSize? { for attribute in self.attributes { switch attribute { case let .Video(_, size): return size case let .ImageSize(size): return size default: break } } return nil } public func isEqual(_ other: Media) -> Bool { if let other = other as? TelegramMediaFile { if self.fileId == other.fileId { return true } } return false } } public func ==(lhs: TelegramMediaFile, rhs: TelegramMediaFile) -> Bool { return lhs.isEqual(rhs) } public func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAttribute]) -> [TelegramMediaFileAttribute] { var result: [TelegramMediaFileAttribute] = [] for attribute in attributes { switch attribute { case let .documentAttributeFilename(fileName): result.append(.FileName(fileName: fileName)) case let .documentAttributeSticker(flags, alt, stickerSet, maskCoords): result.append(.Sticker(displayText: alt)) case .documentAttributeHasStickers: result.append(.HasLinkedStickers) case let .documentAttributeImageSize(w, h): result.append(.ImageSize(size: CGSize(width: CGFloat(w), height: CGFloat(h)))) case .documentAttributeAnimated: result.append(.Animated) case let .documentAttributeVideo(duration, w, h): result.append(.Video(duration: Int(duration), size: CGSize(width: CGFloat(w), height: CGFloat(h)))) case let .documentAttributeAudio(flags, duration, title, performer, waveform): let isVoice = (flags & (1 << 10)) != 0 var waveformBuffer: MemoryBuffer? if let waveform = waveform { let memory = malloc(waveform.size)! memcpy(memory, waveform.data, waveform.size) waveformBuffer = MemoryBuffer(memory: memory, capacity: waveform.size, length: waveform.size, freeWhenDone: true) } result.append(.Audio(isVoice: isVoice, duration: Int(duration), title: title, performer: performer, waveform: waveformBuffer)) } } return result } public func telegramMediaFileFromApiDocument(_ document: Api.Document) -> TelegramMediaFile? { switch document { case let .document(id, accessHash, _, mimeType, size, thumb, dcId, _, attributes): return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size)), previewRepresentations: telegramMediaImageRepresentationsFromApiSizes([thumb]), mimeType: mimeType, size: Int(size), attributes: telegramMediaFileAttributesFromApiAttributes(attributes)) case .documentEmpty: return nil } }