Home telegram - api.photo
Post
Cancel

telegram - api.photo

uchatMediaImageFromApiPhoto

1
2
3
4
5
6
7
8
9
func uchatMediaImageFromApiPhoto(_ photo: Api.Photo) -> UChatMediaImage? {
    switch photo {
        case let .photo(_, id, accessHash, fileReference, _, sizes, dcId):
            let (immediateThumbnailData, representations) = uchatMediaImageRepresentationsFromApiSizes(datacenterId: dcId, photoId: id, accessHash: accessHash, fileReference: fileReference.makeData(), sizes: sizes)
            return UChatMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: id), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: .cloud(imageId: id, accessHash: accessHash, fileReference: fileReference.makeData()), partialReference: nil)
        case .photoEmpty:
            return nil
    }
}

uchatMediaImageRepresentationsFromApiSizes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func uchatMediaImageRepresentationsFromApiSizes(datacenterId: Int32, photoId: Int64, accessHash: Int64, fileReference: Data?, sizes: [Api.PhotoSize]) -> (immediateThumbnail: Data?, representations:  [UChatMediaImageRepresentation]) {
    var immediateThumbnailData: Data?
    var representations: [UChatMediaImageRepresentation] = []
    for size in sizes {
        switch size {
            case let .photoCachedSize(type, location, w, h, _):
                switch location {
                    case let .fileLocationToBeDeprecated(volumeId, localId):
                        let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference)
                        representations.append(UChatMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
                }
            case let .photoSize(type, location, w, h, _):
                switch location {
                    case let .fileLocationToBeDeprecated(volumeId, localId):
                        let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference)
                        representations.append(UChatMediaImageRepresentation(dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), resource: resource))
                }
            case let .photoStrippedSize(_, data):
                immediateThumbnailData = data.makeData()
            case .photoSizeEmpty:
                break
        }
    }
    return (immediateThumbnailData, representations)
}

Api.PhotoSize

1
2
3
4
5
6
public enum PhotoSize: TypeConstructorDescription {
    case photoSizeEmpty(type: String)
    case photoSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, size: Int32)
    case photoCachedSize(type: String, location: Api.FileLocation, w: Int32, h: Int32, bytes: Buffer)
    case photoStrippedSize(type: String, bytes: Buffer)
}

parseMediaData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public func parseMediaData(data: Data) -> Media? {
    let buffer = BufferReader(Buffer(data: data))
    var parseBuffer: Buffer?
    guard let signature = buffer.readInt32() else {
        return nil
    }
    if signature == 0x3072cfa1 {
        parseBuffer = parseBytes(buffer).flatMap({ $0.makeData() }).flatMap(MTGzip.decompress).flatMap(Buffer.init(data:))
    } else {
        parseBuffer = Buffer(data: data)
    }
    
    if let parseBuffer = parseBuffer, let object = Api.parse(parseBuffer) {
        if let photo = object as? Api.Photo {
            return uchatMediaImageFromApiPhoto(photo)
        } else if let document = object as? Api.Document {
            return uchatMediaFileFromApiDocument(document)
        }
    }
    return nil
}

collectPreCachedResources

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private func collectPreCachedResources(for photo: Api.Photo) -> [(MediaResource, Data)]? {
    switch photo {
        case let .photo(_, id, accessHash, fileReference, _, sizes, dcId):
            for size in sizes {
                switch size {
                    case let .photoCachedSize(type, location, _, _, bytes):
                        switch location {
                            case let .fileLocationToBeDeprecated(volumeId, localId):
                                let resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference.makeData())
                                let data = bytes.makeData()
                                return [(resource, data)]
                        }
                    default:
                        break
                }
            }
            return nil
        case .photoEmpty:
            return nil
    }
}

TelegramMediaImage

1
2
3
4
5
6
7
8
public let imageId: MediaId
public let representations: [TelegramMediaImageRepresentation]
public let videoRepresentations: [TelegramMediaImage.VideoRepresentation]
public let immediateThumbnailData: Data?
public let reference: TelegramMediaImageReference?
public let partialReference: PartialMediaReference?
public let peerIds: [PeerId] = []
public let flags: TelegramMediaImageFlags

UChatPeerPhoto

1
2
3
4
5
6
7
8
public struct UChatPeerPhoto {
    public let image: UChatMediaImage
    public let date: Int32
    public let reference: UChatMediaImageReference?
    public let index: Int
    public let totalCount: Int
    public let messageId: MessageId?
}

UChatMediaImageReference

1
case cloud(imageId: Int64, accessHash: Int64, fileReference: Data?)

requestPeerPhotos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
public func requestPeerPhotos(account:Account, peerId: PeerId) -> Signal<[UChatPeerPhoto], NoError> {
    return account.postbox.transaction{ transaction -> Peer? in
        return transaction.getPeer(peerId)
    }
    |> mapToSignal { peer -> Signal<[UChatPeerPhoto], NoError> in
        if let peer = peer as? UChatUser, let inputUser = apiInputUser(peer) {
            return account.network.request(Api.functions.photos.getUserPhotos(userId: inputUser, offset: 0, maxId: 0, limit: 100))
            |> map {Optional($0)}
            |> mapError {_ in}
            |> `catch` { _ -> Signal<Api.photos.Photos?, NoError> in
                return .single(nil)
            }
            |> map { result -> [UChatPeerPhoto] in
                if let result = result {
                    let totalCount:Int
                    let photos: [Api.Photo]
                    switch result {
                        case let .photos(data):
                            photos = data.photos
                            totalCount = photos.count
                        case let .photosSlice(data):
                            photos = data.photos
                            totalCount = Int(data.count)
                    }
                    
                    var images: [UChatPeerPhoto] = []
                    for i in 0 ..< photos.count {
                        if let image = uchatMediaImageFromApiPhoto(photos[i]), let reference = image.reference {
                            var date: Int32 = 0
                            switch photos[i] {
                                case let .photo(_, _, _, _, apiDate, _, _):
                                    date = apiDate
                                case .photoEmpty:
                                    break
                            }
                            images.append(UChatPeerPhoto(image: image, date: date, reference: reference, index: i, totalCount: totalCount, messageId: nil))
                        }
                    }
                    
                    return images
                } else {
                    return []
                }
            }
        } else if let peer = peer, let inputPeer = apiInputPeer(peer) {
            return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0))
            |> map(Optional.init)
            |> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
                return .single(nil)
            }
            |> mapToSignal { result -> Signal<[UChatPeerPhoto], NoError> in
                if let result = result {
                    let messages: [Api.Message]
                    let chats: [Api.Chat]
                    let users: [Api.User]
                    switch result {
                        case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
                            messages = apiMessages
                            chats = apiChats
                            users = apiUsers
                        case let .messages(apiMessages, apiChats, apiUsers):
                            messages = apiMessages
                            chats = apiChats
                            users = apiUsers
                        case let .messagesSlice(_, _, _, apiMessages, apiChats, apiUsers):
                            messages = apiMessages
                            chats = apiChats
                            users = apiUsers
                        case .messagesNotModified:
                            messages = []
                            chats = []
                            users = []
                    }
                    
                    return account.postbox.transaction { transaction -> [Message] in
                        var peers: [PeerId: Peer] = [:]
                        
                        for user in users {
                            if let user = UChatUser.merge(transaction.getPeer(user.peerId) as? UChatUser, rhs: user) {
                                peers[user.id] = user
                            }
                        }
                        
                        for chat in chats {
                            if let groupOrChannel = parseUChatGroupOrChannel(chat: chat) {
                                peers[groupOrChannel.id] = groupOrChannel
                            }
                        }
                        
                        var renderedMessages: [Message] = []
                        for message in messages {
                            if let message = StoreMessage(apiMessage: message), let renderedMessage = locallyRenderedMessage(message: message, peers: peers) {
                                renderedMessages.append(renderedMessage)
                            }
                        }
                        
                        return renderedMessages
                    } |> map { messages -> [UChatPeerPhoto] in
                        var photos: [UChatPeerPhoto] = []
                        var index:Int = 0
                        for message in messages {
                            if let media = message.media.first as? UChatMediaAction {
                                switch media.action {
                                    case let .photoUpdated(image):
                                        if let image = image {
                                            photos.append(UChatPeerPhoto(image: image, date: message.timestamp, reference: nil, index: index, totalCount: messages.count, messageId: message.id))
                                        }
                                    default:
                                        break
                                }
                            }
                            index += 1
                        }
                        return photos
                    }
                    
                } else {
                    return .single([])
                }
            }
        } else {
            return .single([])
        }
    }
}

struct Api.photos

1
2
3
4
5
6
7
8
public enum Photo: TypeConstructorDescription {
    case photo(photo: Api.Photo, users: [Api.User])
}

public enum Photos: TypeConstructorDescription {
    case photos(photos: [Api.Photo], users: [Api.User])
    case photosSlice(count: Int32, photos: [Api.Photo], users: [Api.User])
}

peerAvatarImage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public func peerAvatarImage(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: UChatMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), inset: CGFloat = 0.0, clipStyle: AvatarNodeClipStyle = .circle, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, grayishImage: Bool = false) -> Signal<UIImage?, NoError>? {
    if let imageData = peerAvatarImageData(account: account, peer: peer, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) {
        return imageData
        |> mapToSignal { data -> Signal<UIImage?, NoError> in
            let generate = deferred { () -> Signal<UIImage?, NoError> in
                if emptyColor == nil && data == nil {
                    return .single(nil)
                }
                let image = generateImage(displayDimensions, contextGenerator: { size, context -> Void in
                    if let data = data {
                        if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
                            if grayishImage, let grayImage = generateGrayishImage(image: UIImage(cgImage: dataImage)) {
                                context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
                                context.setBlendMode(.luminosity)
                                context.draw(grayImage.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                                context.setBlendMode(.destinationOut)
                            } else {
                                context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
                                context.setBlendMode(.copy)
                                context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                                context.setBlendMode(.destinationOut)
                            }
                            if case .circle = clipStyle {
                                context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                            } else if case .round = clipStyle {
                                context.draw(rectCorners(7.5).cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                            } else if case .corner(let radius) = clipStyle {
                                context.draw(rectCorners(radius).cgImage!, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                            }
                        } else {
                            if let emptyColor = emptyColor {
                                context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
                                context.setFillColor(emptyColor.cgColor)
                                context.beginPath()
                                if case .circle = clipStyle {
                                    context.addEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                                } else if case .corner(let radius) = clipStyle {
                                    context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: radius).cgPath)
                                } else if case .round = clipStyle {
                                    context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: 7.5).cgPath)
                                }
                                context.fillPath()
                            }
                        }
                    } else if let emptyColor = emptyColor {
                        context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
                        context.setFillColor(emptyColor.cgColor)
                        context.beginPath()
                        if case .circle = clipStyle {
                            context.addEllipse(in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
                        } else if case .corner(let radius) = clipStyle {
                            context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: radius).cgPath)
                        } else if case .round = clipStyle {
                            context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: 7.5).cgPath)
                        }
                        context.fillPath()
                    }
                })
                return .single(image)
            }
            if synchronousLoad {
                return generate
            } else {
                return generate |> runOn(Queue.concurrentDefaultQueue())
            }
        }
    } else {
        return nil
    }
}

peerAvatarImageData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public func peerAvatarImageData(account: Account, peer: Peer, authorOfMessage: MessageReference?, representation: UChatMediaImageRepresentation?, synchronousLoad: Bool) -> Signal<Data?, NoError>? {
    if let smallProfileImage = representation {
        let resourceData = account.postbox.mediaBox.resourceData(smallProfileImage.resource, attemptSynchronously: synchronousLoad)
        let imageData = resourceData
        |> take(1)
        |> mapToSignal { maybeData -> Signal<Data?, NoError> in
            if maybeData.complete {
                return .single(try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)))
            } else {
                return Signal { subscriber in
                    let resourceDataDisposable = resourceData.start(next: { data in
                        if data.complete {
                            subscriber.putNext(try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)))
                            subscriber.putCompletion()
                        } else {
                            subscriber.putNext(nil)
                        }
                    }, error: { error in
                        subscriber.putError(error)
                    }, completed: {
                        subscriber.putCompletion()
                    })
                    var fetchedDataDisposable: Disposable?
                    if let peerReference = PeerReference(peer) {
                        fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .avatar(peer: peerReference, resource: smallProfileImage.resource), statsCategory: .generic).start()
                    } else if let authorOfMessage = authorOfMessage {
                        fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .messageAuthorAvatar(message: authorOfMessage, resource: smallProfileImage.resource), statsCategory: .generic).start()
                    } else {
                        fetchedDataDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: .standalone(resource: smallProfileImage.resource), statsCategory: .generic).start()
                    }
                    return ActionDisposable {
                        resourceDataDisposable.dispose()
                        fetchedDataDisposable?.dispose()
                    }
                }
            }
        }
        return imageData
    } else {
        return nil
    }
}
This post is licensed under CC BY 4.0 by the author.