From f3772cffa747841f9d3b9783acb769ff6bfdf923 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 10 Jul 2020 19:44:18 +0300 Subject: [PATCH] Video avatar fixes --- .../Sources/LegacyAvatarPicker.swift | 2 +- .../Sources/LegacyPaintStickersContext.swift | 6 +- .../TelegramCore/Sources/Authorization.swift | 6 +- .../Sources/PeerPhotoUpdater.swift | 24 +++++-- .../AuthorizationSequenceController.swift | 65 ++++++++++++++++++- ...uthorizationSequenceSignUpController.swift | 13 +++- 6 files changed, 98 insertions(+), 18 deletions(-) diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift index 4e49a364b2..1b33615447 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift @@ -32,7 +32,7 @@ public func presentLegacyAvatarEditor(theme: PresentationTheme, image: UIImage?, }) } -public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void) { +public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void, videoCompletion: @escaping (UIImage, URL, TGVideoEditAdjustments?) -> Void = { _, _, _ in}) { let legacyController = LegacyController(presentation: .custom, theme: theme) legacyController.statusBar.statusBarStyle = .Ignore diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift index 81cc38e395..0d8c690267 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyPaintStickersContext.swift @@ -269,14 +269,14 @@ private class LegacyPaintTextEntity: LegacyPaintEntity { } public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRenderer { - private let account: Account + private let account: Account? private let queue = Queue() private let entities: [LegacyPaintEntity] private let originalSize: CGSize private let cropRect: CGRect? - public init(account: Account, adjustments: TGMediaEditAdjustments) { + public init(account: Account?, adjustments: TGMediaEditAdjustments) { self.account = account self.originalSize = adjustments.originalSize self.cropRect = adjustments.cropRect.isEmpty ? nil : adjustments.cropRect @@ -285,7 +285,7 @@ public final class LegacyPaintEntityRenderer: NSObject, TGPhotoPaintEntityRender if let paintingData = adjustments.paintingData, let paintingEntities = paintingData.entities { for paintingEntity in paintingEntities { if let sticker = paintingEntity as? TGPhotoPaintStickerEntity { - if let entity = LegacyPaintStickerEntity(account: account, entity: sticker) { + if let account = account, let entity = LegacyPaintStickerEntity(account: account, entity: sticker) { entities.append(entity) } } else if let text = paintingEntity as? TGPhotoPaintTextEntity { diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 1334516529..9657f9b5f9 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -430,7 +430,7 @@ public enum SignUpError { case invalidLastName } -public func signUpWithName(accountManager: AccountManager, account: UnauthorizedAccount, firstName: String, lastName: String, avatarData: Data?) -> Signal { +public func signUpWithName(accountManager: AccountManager, account: UnauthorizedAccount, firstName: String, lastName: String, avatarData: Data?, avatarVideo: Signal?, videoStartTimestamp: Double?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let state = transaction.getState() as? UnauthorizedAccountState, case let .signUp(number, codeHash, _, _, _, syncContacts) = state.contents { return account.network.request(Api.functions.auth.signUp(phoneNumber: number, phoneCodeHash: codeHash, firstName: firstName, lastName: lastName)) @@ -470,7 +470,7 @@ public func signUpWithName(accountManager: AccountManager, account: Unauthorized let resource = LocalFileMediaResource(fileId: arc4random64()) account.postbox.mediaBox.storeResourceData(resource.id, data: avatarData) - return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource), video: nil, videoStartTimestamp: nil, mapResourceToAvatarSizes: { _, _ in .single([:]) }) + return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource), video: avatarVideo, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { _, _ in .single([:]) }) |> `catch` { _ -> Signal in return .complete() } @@ -486,7 +486,7 @@ public func signUpWithName(accountManager: AccountManager, account: Unauthorized |> then(switchedAccounts) } else { return appliedState - |> then(switchedAccounts) + |> then(switchedAccounts) } case .authorizationSignUpRequired: return .fail(.generic) diff --git a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift index 1ab7a723fb..d8b93c3aa2 100644 --- a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift +++ b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift @@ -47,13 +47,23 @@ public func uploadedPeerPhoto(postbox: Postbox, network: Network, resource: Medi } } -public func uploadedPeerVideo(postbox: Postbox, network: Network, messageMediaPreuploadManager: MessageMediaPreuploadManager, resource: MediaResource) -> Signal { - return messageMediaPreuploadManager.upload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .video), hintFileSize: nil, hintFileIsLarge: false) - |> map { result -> UploadedPeerPhotoData in - return UploadedPeerPhotoData(resource: resource, content: .result(result)) - } - |> `catch` { _ -> Signal in - return .single(UploadedPeerPhotoData(resource: resource, content: .error)) +public func uploadedPeerVideo(postbox: Postbox, network: Network, messageMediaPreuploadManager: MessageMediaPreuploadManager?, resource: MediaResource) -> Signal { + if let messageMediaPreuploadManager = messageMediaPreuploadManager { + return messageMediaPreuploadManager.upload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .video), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedPeerPhotoData in + return UploadedPeerPhotoData(resource: resource, content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedPeerPhotoData(resource: resource, content: .error)) + } + } else { + return multipartUpload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .video), hintFileSize: nil, hintFileIsLarge: false) + |> map { result -> UploadedPeerPhotoData in + return UploadedPeerPhotoData(resource: resource, content: .result(result)) + } + |> `catch` { _ -> Signal in + return .single(UploadedPeerPhotoData(resource: resource, content: .error)) + } } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift index fca4df758f..63caa0c921 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift @@ -15,6 +15,8 @@ import AccountContext import CountrySelectionUI import SettingsUI import PhoneNumberFormat +import LegacyComponents +import LegacyMediaPickerUI private enum InnerState: Equatable { case state(UnauthorizedAccountStateContents) @@ -684,11 +686,70 @@ public final class AuthorizationSequenceController: NavigationController, MFMail transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))) }).start() }, displayCancel: displayCancel) - controller.signUpWithName = { [weak self, weak controller] firstName, lastName, avatarData in + controller.signUpWithName = { [weak self, weak controller] firstName, lastName, avatarData, avatarUrl, avatarAdjustments in if let strongSelf = self { controller?.inProgress = true - strongSelf.actionDisposable.set((signUpWithName(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData) + var videoStartTimestamp: Double? = nil + if let adjustments = avatarAdjustments, adjustments.videoStartValue > 0.0 { + videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue + } + + let avatarVideo: Signal? + if let avatarUrl = avatarUrl { + let account = strongSelf.account + avatarVideo = Signal { subscriber in + var filteredPath = avatarUrl.path + if filteredPath.hasPrefix("file://") { + filteredPath = String(filteredPath[filteredPath.index(filteredPath.startIndex, offsetBy: "file://".count)]) + } + + let avAsset = AVURLAsset(url: URL(fileURLWithPath: filteredPath)) + let entityRenderer: LegacyPaintEntityRenderer? = avatarAdjustments.flatMap { adjustments in + if let paintingData = adjustments.paintingData, paintingData.hasAnimation { + return LegacyPaintEntityRenderer(account: nil, adjustments: adjustments) + } else { + return nil + } + } + + let signal = TGMediaVideoConverter.convert(avAsset, adjustments: avatarAdjustments, watcher: nil, entityRenderer: entityRenderer)! + + let signalDisposable = signal.start(next: { next in + if let result = next as? TGMediaVideoConversionResult { + var value = stat() + if stat(result.fileURL.path, &value) == 0 { + if let data = try? Data(contentsOf: result.fileURL) { + let resource = LocalFileMediaResource(fileId: arc4random64()) + account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) + subscriber.putNext(resource) + } + } + subscriber.putCompletion() + } + }, error: { _ in + }, completed: nil) + + let disposable = ActionDisposable { + signalDisposable?.dispose() + } + + return ActionDisposable { + disposable.dispose() + } + } + |> mapToSignal { resource -> Signal in + if let resource = resource { + return uploadedPeerVideo(postbox: account.postbox, network: account.network, messageMediaPreuploadManager: nil, resource: resource) |> map(Optional.init) + } else { + return .single(nil) + } + } + } else { + avatarVideo = nil + } + + strongSelf.actionDisposable.set((signUpWithName(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData, avatarVideo: avatarVideo, videoStartTimestamp: videoStartTimestamp) |> deliverOnMainQueue).start(error: { error in Queue.mainQueue().async { if let strongSelf = self, let controller = controller { diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift index 0c4d3c8240..6120dfa3f7 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceSignUpController.swift @@ -22,7 +22,10 @@ final class AuthorizationSequenceSignUpController: ViewController { var initialName: (String, String) = ("", "") private var termsOfService: UnauthorizedAccountTermsOfService? - var signUpWithName: ((String, String, Data?) -> Void)? + var signUpWithName: ((String, String, Data?, URL?, TGVideoEditAdjustments?) -> Void)? + + var avatarUrl: URL? + var avatarAdjustments: TGVideoEditAdjustments? private let hapticFeedback = HapticFeedback() @@ -88,6 +91,12 @@ final class AuthorizationSequenceSignUpController: ViewController { self?.present(c, in: .window(.root), with: a) }, openCurrent: nil, completion: { image in self?.controllerNode.currentPhoto = image + self?.avatarUrl = nil + self?.avatarAdjustments = nil + }, videoCompletion: { image, url, adjustments in + self?.controllerNode.currentPhoto = image + self?.avatarUrl = url + self?.avatarAdjustments = adjustments }) }) self.displayNodeDidLoad() @@ -150,7 +159,7 @@ final class AuthorizationSequenceSignUpController: ViewController { if let name = name { self.signUpWithName?(name.0, name.1, self.controllerNode.currentPhoto.flatMap({ image in return compressImageToJPEG(image, quality: 0.7) - })) + }), self.avatarUrl, self.avatarAdjustments) } } }