diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 70daf23f13..33a38f754a 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -157,7 +157,7 @@ public func galleryItemForEntry(context: AccountContext, presentationData: Prese if true || (file.mimeType == "video/mpeg4" || file.mimeType == "video/mov" || file.mimeType == "video/mp4") { content = NativeVideoContent(id: .message(message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath) } else { - content = PlatformVideoContent(id: .message(message.id, message.stableId, file.fileId), fileReference: .message(message: MessageReference(message), media: file), streamVideo: streamVideos, loopVideo: loopVideos) + content = PlatformVideoContent(id: .message(message.id, message.stableId, file.fileId), content: .file(.message(message: MessageReference(message), media: file)), streamVideo: streamVideos, loopVideo: loopVideos) } } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 36a6a8e203..75bda68d39 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -18,7 +18,7 @@ import AppBundle public enum UniversalVideoGalleryItemContentInfo { case message(Message) - case webPage(TelegramMediaWebpage, Media) + case webPage(TelegramMediaWebpage, Media, ((@escaping () -> GalleryTransitionArguments?, NavigationController?, (ViewController, Any?) -> Void) -> Void)?) } public class UniversalVideoGalleryItem: GalleryItem { @@ -108,7 +108,7 @@ public class UniversalVideoGalleryItem: GalleryItem { } } } - } else if case let .webPage(webPage, media) = contentInfo, let file = media as? TelegramMediaFile { + } else if case let .webPage(webPage, media, _) = contentInfo, let file = media as? TelegramMediaFile { if let item = ChatMediaGalleryThumbnailItem(account: self.context.account, mediaReference: .webPage(webPage: WebpageReference(webPage), media: file)) { return (0, item) } @@ -470,6 +470,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var disablePictureInPicture = false var disablePlayerControls = false + var forceEnablePiP = false var isAnimated = false if let content = item.content as? NativeVideoContent { isAnimated = content.fileReference.media.isAnimated @@ -487,6 +488,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { default: break } + } else if let _ = item.content as? PlatformVideoContent { + disablePlayerControls = true + forceEnablePiP = true } if let videoNode = self.videoNode { @@ -511,7 +515,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { strongSelf.playOnContentOwnership = false strongSelf.initiallyActivated = true strongSelf.skipInitialPause = true - strongSelf.videoNode?.playOnceWithSound(playAndRecord: false, actionAtEnd: isAnimated ? .loop : .stop) + if let item = strongSelf.item, let _ = item.content as? PlatformVideoContent { + strongSelf.videoNode?.play() + } else { + strongSelf.videoNode?.playOnceWithSound(playAndRecord: false, actionAtEnd: isAnimated ? .loop : .stop) + } } } } @@ -698,7 +706,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { let rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Media Gallery/Stickers"), color: .white), style: .plain, target: self, action: #selector(self.openStickersButtonPressed)) barButtonItems.append(rightBarButtonItem) } - if !isAnimated && !disablePlayerControls && !disablePictureInPicture { + if forceEnablePiP || (!isAnimated && !disablePlayerControls && !disablePictureInPicture) { let rightBarButtonItem = UIBarButtonItem(image: pictureInPictureButtonImage, style: .plain, target: self, action: #selector(self.pictureInPictureButtonPressed)) barButtonItems.append(rightBarButtonItem) self.hasPictureInPicture = true @@ -725,9 +733,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { switch contentInfo { case let .message(message): self.footerContentNode.setMessage(message) - case let .webPage(webPage, media): + case let .webPage(webPage, media, _): self.footerContentNode.setWebPage(webPage, media: media) - break } } self.footerContentNode.setup(origin: item.originData, caption: item.caption) @@ -757,7 +764,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } private func shouldAutoplayOnCentrality() -> Bool { -// !self.initiallyActivated if let item = self.item, let content = item.content as? NativeVideoContent { var isLocal = false if let fetchStatus = self.fetchStatus, case .Local = fetchStatus { @@ -772,6 +778,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if isLocal || isStreamable { return true } + } else if let item = self.item, let _ = item.content as? PlatformVideoContent { + return true } return false } @@ -1335,8 +1343,27 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } return nil })) - case .webPage: - break + case let .webPage(_, _, expandFromPip): + if let expandFromPip = expandFromPip, let baseNavigationController = baseNavigationController { + expandFromPip({ [weak overlayNode] in + if let overlayNode = overlayNode, let overlaySupernode = overlayNode.supernode { + return GalleryTransitionArguments(transitionNode: (overlayNode, overlayNode.bounds, { [weak overlayNode] in + return (overlayNode?.view.snapshotContentTree(), nil) + }), addToTransitionSurface: { [weak context, weak overlaySupernode, weak overlayNode] view in + guard let context = context, let overlayNode = overlayNode else { + return + } + if context.sharedContext.mediaManager.hasOverlayVideoNode(overlayNode) { + overlaySupernode?.view.addSubview(view) + } + overlayNode.canAttachContent = false + }) + } + return nil + }, baseNavigationController, { [weak baseNavigationController] c, a in + (baseNavigationController?.topViewController as? ViewController)?.present(c, in: .window(.root), with: a) + }) + } } } if customUnembedWhenPortrait(overlayNode) { @@ -1398,8 +1425,27 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } return nil })) - case .webPage: - break + case let .webPage(_, _, expandFromPip): + if let expandFromPip = expandFromPip, let baseNavigationController = baseNavigationController { + expandFromPip({ [weak overlayNode] in + if let overlayNode = overlayNode, let overlaySupernode = overlayNode.supernode { + return GalleryTransitionArguments(transitionNode: (overlayNode, overlayNode.bounds, { [weak overlayNode] in + return (overlayNode?.view.snapshotContentTree(), nil) + }), addToTransitionSurface: { [weak context, weak overlaySupernode, weak overlayNode] view in + guard let context = context, let overlayNode = overlayNode else { + return + } + if context.sharedContext.mediaManager.hasOverlayVideoNode(overlayNode) { + overlaySupernode?.view.addSubview(view) + } + overlayNode.canAttachContent = false + }) + } + return nil + }, baseNavigationController, { [weak baseNavigationController] c, a in + (baseNavigationController?.topViewController as? ViewController)?.present(c, in: .window(.root), with: a) + }) + } } } context.sharedContext.mediaManager.setOverlayVideoNode(overlayNode) diff --git a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift index 7a7cdc3a09..b135b65242 100644 --- a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift +++ b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift @@ -113,7 +113,7 @@ public struct InstantPageGalleryEntry: Equatable { nativeId = .instantPage(self.pageId, file.fileId) } - return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: nativeId, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), streamVideo: isMediaStreamable(media: file) ? .conservative : .none), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in }, storeMediaPlaybackState: { _, _ in }, present: { _, _ in }) + return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: NativeVideoContent(id: nativeId, fileReference: .webPage(webPage: WebpageReference(webPage), media: file), streamVideo: isMediaStreamable(media: file) ? .conservative : .none), originData: nil, indexData: indexData, contentInfo: .webPage(webPage, file, nil), caption: caption, credit: credit, fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in }, storeMediaPlaybackState: { _, _ in }, present: { _, _ in }) } else { var representations: [TelegramMediaImageRepresentation] = [] representations.append(contentsOf: file.previewRepresentations) @@ -124,10 +124,24 @@ public struct InstantPageGalleryEntry: Equatable { return InstantImageGalleryItem(context: context, presentationData: presentationData, itemId: self.index, imageReference: .webPage(webPage: WebpageReference(webPage), media: image), caption: caption, credit: credit, location: self.location, openUrl: openUrl, openUrlOptions: openUrlOptions) } } else if let embedWebpage = self.media.media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = embedWebpage.content { - if let content = WebEmbedVideoContent(webPage: embedWebpage, webpageContent: webpageContent) { - return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in }, storeMediaPlaybackState: { _, _ in }, present: { _, _ in }) + if webpageContent.url.hasSuffix(".m3u8") { + let content = PlatformVideoContent(id: .instantPage(embedWebpage.webpageId, embedWebpage.webpageId), content: .url(webpageContent.url), streamVideo: true, loopVideo: false) + return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage, { makeArguments, navigationController, present in + let gallery = InstantPageGalleryController(context: context, webPage: webPage, entries: [self], centralIndex: 0, replaceRootController: { [weak navigationController] controller, ready in + if let navigationController = navigationController { + navigationController.replaceTopController(controller, animated: false, ready: ready) + } + }, baseNavigationController: navigationController) + present(gallery, InstantPageGalleryControllerPresentationArguments(transitionArguments: { entry -> GalleryTransitionArguments? in + return makeArguments() + })) + }), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in }, storeMediaPlaybackState: { _, _ in }, present: { _, _ in }) } else { - preconditionFailure() + if let content = WebEmbedVideoContent(webPage: embedWebpage, webpageContent: webpageContent) { + return UniversalVideoGalleryItem(context: context, presentationData: presentationData, content: content, originData: nil, indexData: nil, contentInfo: .webPage(webPage, embedWebpage, nil), caption: NSAttributedString(string: ""), fromPlayingVideo: fromPlayingVideo, landscape: landscape, performAction: { _ in }, openActionOptions: { _ in }, storeMediaPlaybackState: { _, _ in }, present: { _, _ in }) + } else { + preconditionFailure() + } } } else { preconditionFailure() @@ -169,6 +183,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable private let centralItemTitle = Promise() private let centralItemTitleView = Promise() private let centralItemRightBarButtonItem = Promise() + private let centralItemRightBarButtonItems = Promise<[UIBarButtonItem]?>(nil) private let centralItemNavigationStyle = Promise() private let centralItemFooterContentNode = Promise<(GalleryFooterContentNode?, GalleryOverlayContentNode?)>() private let centralItemAttributesDisposable = DisposableSet(); @@ -239,8 +254,15 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable self?.navigationItem.titleView = titleView })) - self.centralItemAttributesDisposable.add(self.centralItemRightBarButtonItem.get().start(next: { [weak self] rightBarButtonItem in - self?.navigationItem.rightBarButtonItem = rightBarButtonItem + self.centralItemAttributesDisposable.add(combineLatest(self.centralItemRightBarButtonItem.get(), self.centralItemRightBarButtonItems.get()).start(next: { [weak self] rightBarButtonItem, rightBarButtonItems in + if let rightBarButtonItem = rightBarButtonItem { + self?.navigationItem.rightBarButtonItem = rightBarButtonItem + } else if let rightBarButtonItems = rightBarButtonItems { + self?.navigationItem.rightBarButtonItems = rightBarButtonItems + } else { + self?.navigationItem.rightBarButtonItem = nil + self?.navigationItem.rightBarButtonItems = nil + } })) self.centralItemAttributesDisposable.add(self.centralItemFooterContentNode.get().start(next: { [weak self] footerContentNode, _ in @@ -362,6 +384,11 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable self?.presentingViewController?.dismiss(animated: false, completion: nil) } + self.galleryNode.completeCustomDismiss = { [weak self] in + self?._hiddenMedia.set(.single(nil)) + self?.presentingViewController?.dismiss(animated: false, completion: nil) + } + self.galleryNode.pager.replaceItems(self.entries.map({ $0.item(context: self.context, webPage: self.webPage, message: self.message, presentationData: self.presentationData, fromPlayingVideo: self.fromPlayingVideo, landscape: self.landscape, openUrl: self.innerOpenUrl, openUrlOptions: self.openUrlOptions) }), centralItemIndex: self.centralEntryIndex) @@ -376,6 +403,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable strongSelf.centralItemTitle.set(node.title()) strongSelf.centralItemTitleView.set(node.titleView()) strongSelf.centralItemRightBarButtonItem.set(node.rightBarButtonItem()) + strongSelf.centralItemRightBarButtonItems.set(node.rightBarButtonItems()) strongSelf.centralItemNavigationStyle.set(node.navigationStyle()) strongSelf.centralItemFooterContentNode.set(node.footerContent()) } @@ -386,6 +414,11 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable } } + let baseNavigationController = self.baseNavigationController + self.galleryNode.baseNavigationController = { [weak baseNavigationController] in + return baseNavigationController + } + let ready = self.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak self] _ in self?.didSetReady = true } @@ -401,6 +434,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable self.centralItemTitle.set(centralItemNode.title()) self.centralItemTitleView.set(centralItemNode.titleView()) self.centralItemRightBarButtonItem.set(centralItemNode.rightBarButtonItem()) + self.centralItemRightBarButtonItems.set(centralItemNode.rightBarButtonItems()) self.centralItemNavigationStyle.set(centralItemNode.navigationStyle()) self.centralItemFooterContentNode.set(centralItemNode.footerContent()) diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 92e090e841..d06d8a8810 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -70,6 +70,8 @@ private enum DebugControllerEntry: ItemListNodeEntry { case photoPreview(PresentationTheme, Bool) case knockoutWallpaper(PresentationTheme, Bool) case alternativeFolderTabs(Bool) + case playerEmbedding(Bool) + case playlistPlayback(Bool) case videoCalls(Bool) case videoCallsInfo(PresentationTheme, String) case hostInfo(PresentationTheme, String) @@ -85,7 +87,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { return DebugControllerSection.logging.rawValue case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries: return DebugControllerSection.experiments.rawValue - case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs: + case .clearTips, .reimport, .resetData, .resetDatabase, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback: return DebugControllerSection.experiments.rawValue case .videoCalls, .videoCallsInfo: return DebugControllerSection.videoExperiments.rawValue @@ -142,14 +144,18 @@ private enum DebugControllerEntry: ItemListNodeEntry { return 22 case .alternativeFolderTabs: return 23 - case .videoCalls: + case .playerEmbedding: return 24 - case .videoCallsInfo: + case .playlistPlayback: return 25 - case .hostInfo: + case .videoCalls: return 26 - case .versionInfo: + case .videoCallsInfo: return 27 + case .hostInfo: + return 28 + case .versionInfo: + return 29 } } @@ -547,6 +553,26 @@ private enum DebugControllerEntry: ItemListNodeEntry { }) }).start() }) + case let .playerEmbedding(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Player Embedding", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = arguments.sharedContext.accountManager.transaction ({ transaction in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in + var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings + settings.playerEmbedding = value + return settings + }) + }).start() + }) + case let .playlistPlayback(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Playlist Playback", value: value, sectionId: self.section, style: .blocks, updated: { value in + let _ = arguments.sharedContext.accountManager.transaction ({ transaction in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in + var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings + settings.playlistPlayback = value + return settings + }) + }).start() + }) case let .videoCalls(value): return ItemListSwitchItem(presentationData: presentationData, title: "Experimental Feature", value: value, sectionId: self.section, style: .blocks, updated: { value in let _ = arguments.sharedContext.accountManager.transaction ({ transaction in @@ -602,6 +628,8 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS //entries.append(.photoPreview(presentationData.theme, experimentalSettings.chatListPhotos)) entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper)) entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom)) + entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) + entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) entries.append(.videoCalls(experimentalSettings.videoCalls)) entries.append(.videoCallsInfo(presentationData.theme, "Enables experimental transmission of electromagnetic radiation synchronized with pressure waves. Needs to be enabled on both sides.")) diff --git a/submodules/TelegramUI/Resources/Animations/Bin.json b/submodules/TelegramUI/Resources/Animations/Bin.json index 6deddcf180..8eccdf3587 100644 --- a/submodules/TelegramUI/Resources/Animations/Bin.json +++ b/submodules/TelegramUI/Resources/Animations/Bin.json @@ -1 +1 @@ -{"v":"5.6.5","fr":60,"ip":167,"op":251,"w":240,"h":240,"nm":"RedBin","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Cap4","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":199,"s":[0,-3,0],"to":[0,-1.5,0],"ti":[0,1.5,0]},{"t":206,"s":[0,-12,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.83,0],[0,0],[0,-0.83],[0,0],[0,0]],"o":[[0,0],[0,-0.83],[0,0],[0.83,0],[0,0],[0,0],[0,0]],"v":[[-3.5,1.5],[-3.5,0],[-2,-1.5],[2,-1.5],[3.5,0],[3.5,1.5],[3.5,1.5]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cap2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[87.5]},{"t":204,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[12.5]},{"t":204,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Cap3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":206,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":213,"s":[-5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":221,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":228,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":236,"s":[-12]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":241,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":246,"s":[-3]},{"t":251,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.944,"y":0},"o":{"x":0.097,"y":1},"t":194,"s":[120,166.791,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.666,"y":0.291},"o":{"x":0.333,"y":0.733},"t":195,"s":[120,166.158,0],"to":[0,0,0],"ti":[0,6.74,0]},{"i":{"x":0.666,"y":0.584},"o":{"x":0.332,"y":0.45},"t":196,"s":[120,162.092,0],"to":[0,-7.662,0],"ti":[0,12.26,0]},{"i":{"x":0.667,"y":0.367},"o":{"x":0.333,"y":0.658},"t":199,"s":[120,138.022,0],"to":[0,-3.21,0],"ti":[0,3.305,0]},{"i":{"x":0.651,"y":0.659},"o":{"x":0.359,"y":0.423},"t":200,"s":[120,132.963,0],"to":[0,-14.035,0],"ti":[0,19.076,0]},{"i":{"x":0.617,"y":0.897},"o":{"x":0.295,"y":0.531},"t":206,"s":[120,83.951,0],"to":[0,-11.45,0],"ti":[0,-1.544,0]},{"i":{"x":0.703,"y":1},"o":{"x":0.341,"y":0.092},"t":213,"s":[120,53.065,0],"to":[0,2.8,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[120,88.5,0],"to":[0,-5,0],"ti":[0,4.417,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[120,58.5,0],"to":[0,-4.417,0],"ti":[0,-4.083,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":236,"s":[120,62,0],"to":[0,4.083,0],"ti":[0,-0.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":241,"s":[120,83,0],"to":[0,0.667,0],"ti":[0,0.917,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":246,"s":[120,66,0],"to":[0,-0.917,0],"ti":[0,-1.917,0]},{"t":251,"s":[120,77.5,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.5,0],[8.5,0]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cap1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":194,"s":[50]},{"t":199,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":194,"s":[50]},{"t":199,"s":[100]}],"ix":2},"o":{"a":0,"k":180,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Line3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":196,"s":[0]},{"t":197,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":197,"s":[139.5,134,0],"to":[0,-0.833,0],"ti":[0,0.833,0]},{"t":198,"s":[139.5,129,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":198,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.667,-3.833],[0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":211,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.667,-3.833],[0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":232,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":241,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.458,-5.083],[-0.042,5.5]],"c":false}]},{"t":247,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[97]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[89.655]},{"t":206,"s":[0]}],"ix":1,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').start;"},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[100]},{"t":206,"s":[100]}],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').end;"},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Line2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":196,"s":[0]},{"t":197,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":197,"s":[120,134,0],"to":[0,-0.833,0],"ti":[0,0.833,0]},{"t":198,"s":[120,129,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":198,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-3.833],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":209,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-3.833],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":240,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.083],[0,5.5]],"c":false}]},{"t":247,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[97]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[89.655]},{"t":206,"s":[0]}],"ix":1,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').start;"},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[100]},{"t":206,"s":[100]}],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').end;"},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-10,"op":590,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Line1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":196,"s":[0]},{"t":197,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":197,"s":[100.5,134,0],"to":[0,-0.833,0],"ti":[0,0.833,0]},{"t":198,"s":[100.5,129,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":198,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.667,-3.833],[-0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":211,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.667,-3.833],[-0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":232,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":241,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.458,-5.083],[0.042,5.5]],"c":false}]},{"t":247,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[97]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[89.655]},{"t":206,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[100]},{"t":206,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Bin","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[0]},{"t":191,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.25,120,0],"ix":2},"a":{"a":0,"k":[0,-9,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.35,"y":1},"o":{"x":0.333,"y":0},"t":200,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.833,-6.833],[-6.943,6.62],[-4.943,8.5],[4.943,8.5],[6.943,6.62],[7.833,-6.833]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.65,"y":0},"t":213,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.833,-6.833],[-6.943,6.62],[-4.943,8.5],[4.943,8.5],[6.943,6.62],[7.833,-6.833]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":230,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":234,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":241,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.417,-7.667],[-6.527,6.62],[-4.527,8.5],[4.527,8.5],[6.527,6.62],[7.417,-7.667]],"c":true}]},{"t":245,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bin","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":189,"s":[50]},{"t":206,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":189,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":206,"s":[73]},{"t":207,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":189,"s":[-43]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[-20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":205,"s":[0.429]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":206,"s":[4]},{"t":207,"s":[-43]}],"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-7,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Recording","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.115,"y":0.996},"o":{"x":0.6,"y":0},"t":166,"s":[120,120,0],"to":[0,-8,0],"ti":[0,-5.661,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.816,"y":0},"t":178,"s":[120,72,0],"to":[0,5.661,0],"ti":[0,-13.661,0]},{"t":192,"s":[120,153.969,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":172,"s":[{"i":[[-2.76,0],[0,2.76],[2.76,0],[0,-2.76]],"o":[[2.76,0],[0,-2.76],[-2.76,0],[0,2.76]],"v":[[0,5],[5,0],[0,-5],[-5,0]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":180,"s":[{"i":[[-2.76,0],[0,2.76],[2.76,0],[0,-2.76]],"o":[[2.76,0],[0,-2.76],[-2.76,0],[0,2.76]],"v":[[0,5],[5,0],[0,-5],[-5,0]],"c":true}]},{"t":192,"s":[{"i":[[-2.547,0.001],[0.008,0.807],[2.735,0.02],[-0.007,-0.787]],"o":[[2.714,-0.001],[0.008,-0.756],[-2.557,0.031],[-0.002,0.812]],"v":[[-0.003,4.99],[2.99,4.317],[-0.003,3.667],[-3.104,4.317]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.231372997165,0.188234999776,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.044,0.036],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Recording","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":220,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.6.5","fr":60,"ip":171,"op":255,"w":240,"h":240,"nm":"RedBin","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Cap4","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":199,"s":[0,-3,0],"to":[0,-1.5,0],"ti":[0,1.5,0]},{"t":206,"s":[0,-12,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-0.83,0],[0,0],[0,-0.83],[0,0],[0,0]],"o":[[0,0],[0,-0.83],[0,0],[0.83,0],[0,0],[0,0],[0,0]],"v":[[-3.5,1.5],[-3.5,0],[-2,-1.5],[2,-1.5],[3.5,0],[3.5,1.5],[3.5,1.5]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cap2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[87.5]},{"t":204,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[12.5]},{"t":204,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Cap3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":206,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":213,"s":[-5]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":221,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":228,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":236,"s":[-12]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":241,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":246,"s":[-3]},{"t":251,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.944,"y":0},"o":{"x":0.097,"y":1},"t":194,"s":[120,166.791,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.666,"y":0.291},"o":{"x":0.333,"y":0.733},"t":195,"s":[120,166.158,0],"to":[0,0,0],"ti":[0,6.74,0]},{"i":{"x":0.666,"y":0.584},"o":{"x":0.332,"y":0.45},"t":196,"s":[120,162.092,0],"to":[0,-7.662,0],"ti":[0,12.26,0]},{"i":{"x":0.667,"y":0.367},"o":{"x":0.333,"y":0.658},"t":199,"s":[120,138.022,0],"to":[0,-3.21,0],"ti":[0,3.305,0]},{"i":{"x":0.651,"y":0.659},"o":{"x":0.359,"y":0.423},"t":200,"s":[120,132.963,0],"to":[0,-14.035,0],"ti":[0,19.076,0]},{"i":{"x":0.617,"y":0.897},"o":{"x":0.295,"y":0.531},"t":206,"s":[120,83.951,0],"to":[0,-11.45,0],"ti":[0,-1.544,0]},{"i":{"x":0.703,"y":1},"o":{"x":0.341,"y":0.092},"t":213,"s":[120,53.065,0],"to":[0,2.8,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[120,88.5,0],"to":[0,-5,0],"ti":[0,4.417,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[120,58.5,0],"to":[0,-4.417,0],"ti":[0,-4.083,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":236,"s":[120,62,0],"to":[0,4.083,0],"ti":[0,-0.667,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":241,"s":[120,83,0],"to":[0,0.667,0],"ti":[0,0.917,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":246,"s":[120,66,0],"to":[0,-0.917,0],"ti":[0,-1.917,0]},{"t":251,"s":[120,77.5,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-8.5,0],[8.5,0]],"c":false},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Cap1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":194,"s":[50]},{"t":199,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":194,"s":[50]},{"t":199,"s":[100]}],"ix":2},"o":{"a":0,"k":180,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Line3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":196,"s":[0]},{"t":197,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":197,"s":[139.5,134,0],"to":[0,-0.833,0],"ti":[0,0.833,0]},{"t":198,"s":[139.5,129,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":198,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.667,-3.833],[0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":211,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.667,-3.833],[0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":232,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":241,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.458,-5.083],[-0.042,5.5]],"c":false}]},{"t":247,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.25,-5.5],[-0.25,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[97]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[89.655]},{"t":206,"s":[0]}],"ix":1,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').start;"},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[100]},{"t":206,"s":[100]}],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').end;"},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Line2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":196,"s":[0]},{"t":197,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":197,"s":[120,134,0],"to":[0,-0.833,0],"ti":[0,0.833,0]},{"t":198,"s":[120,129,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":198,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-3.833],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":209,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-3.833],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":240,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.083],[0,5.5]],"c":false}]},{"t":247,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,-5.5],[0,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[97]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[89.655]},{"t":206,"s":[0]}],"ix":1,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').start;"},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[100]},{"t":206,"s":[100]}],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('Line1').content('\\u041e\\u0431\\u0440\\u0435\\u0437\\u0430\\u0442\\u044c \\u043a\\u043e\\u043d\\u0442\\u0443\\u0440\\u044b 1').end;"},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-10,"op":590,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Line1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":196,"s":[0]},{"t":197,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":197,"s":[100.5,134,0],"to":[0,-0.833,0],"ti":[0,0.833,0]},{"t":198,"s":[100.5,129,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":198,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.667,-3.833],[-0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":211,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.667,-3.833],[-0.167,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":228,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":232,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":241,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.458,-5.083],[0.042,5.5]],"c":false}]},{"t":247,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.25,-5.5],[0.25,5.5]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[97]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[89.655]},{"t":206,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":197,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[100]},{"t":206,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-8,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Bin","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[0]},{"t":191,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.25,120,0],"ix":2},"a":{"a":0,"k":[0,-9,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":189,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.35,"y":1},"o":{"x":0.333,"y":0},"t":200,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.833,-6.833],[-6.943,6.62],[-4.943,8.5],[4.943,8.5],[6.943,6.62],[7.833,-6.833]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.65,"y":0},"t":213,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":221,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.833,-6.833],[-6.943,6.62],[-4.943,8.5],[4.943,8.5],[6.943,6.62],[7.833,-6.833]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":230,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":234,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":241,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7.417,-7.667],[-6.527,6.62],[-4.527,8.5],[4.527,8.5],[6.527,6.62],[7.417,-7.667]],"c":true}]},{"t":245,"s":[{"i":[[0,0],[0,0],[-1.06,0],[0,0],[-0.06,1.05],[0,0]],"o":[[0,0],[0.06,1.05],[0,0],[1.06,0],[0,0],[0,0]],"v":[[-7,-8.5],[-6.11,6.62],[-4.11,8.5],[4.11,8.5],[6.11,6.62],[7,-8.5]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.231372563979,0.188235309077,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bin","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":189,"s":[50]},{"t":206,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":189,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":206,"s":[73]},{"t":207,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":189,"s":[-43]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":199,"s":[-20]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":205,"s":[0.429]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":206,"s":[4]},{"t":207,"s":[-43]}],"ix":3},"m":1,"ix":2,"nm":"Обрезать контуры 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-7,"op":592,"st":-8,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Recording","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.6,"y":0.999},"o":{"x":0.6,"y":0},"t":171,"s":[120,120,0],"to":[0,-8,0],"ti":[0,-5.661,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.4,"y":0},"t":182,"s":[120,72,0],"to":[0,5.661,0],"ti":[0,-13.661,0]},{"t":192,"s":[120,153.969,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":175,"s":[{"i":[[-2.76,0],[0,2.76],[2.76,0],[0,-2.76]],"o":[[2.76,0],[0,-2.76],[-2.76,0],[0,2.76]],"v":[[0,5],[5,0],[0,-5],[-5,0]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":183,"s":[{"i":[[-2.76,0],[0,2.76],[2.76,0],[0,-2.76]],"o":[[2.76,0],[0,-2.76],[-2.76,0],[0,2.76]],"v":[[0,5],[5,0],[0,-5],[-5,0]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":191,"s":[{"i":[[-2.57,0.001],[0.007,1.024],[2.738,0.018],[-0.007,-1.006]],"o":[[2.719,-0.001],[0.007,-0.979],[-2.579,0.027],[-0.002,1.028]],"v":[[-0.003,5.262],[3.213,4.108],[-0.003,2.975],[-3.315,4.108]],"c":true}]},{"t":192,"s":[{"i":[[-2.547,0.001],[0.008,0.807],[2.735,0.02],[-0.007,-0.787]],"o":[[2.714,-0.001],[0.008,-0.756],[-2.557,0.031],[-0.002,0.812]],"v":[[-0.003,4.99],[2.99,4.317],[-0.003,3.667],[-3.104,4.317]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.231372997165,0.188234999776,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.044,0.036],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Recording","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":220,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/BlobView.swift b/submodules/TelegramUI/Sources/BlobView.swift index bf2e47c9f8..57fe0b8bc5 100644 --- a/submodules/TelegramUI/Sources/BlobView.swift +++ b/submodules/TelegramUI/Sources/BlobView.swift @@ -18,7 +18,8 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration maxSpeed: 0.6, minScale: 0.45, maxScale: 0.55, - scaleSpeed: 0.2 + scaleSpeed: 0.2, + isCircle: true ) private let mediumBlob = BlobView( pointsCount: 8, @@ -28,7 +29,8 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration maxSpeed: 7, minScale: 0.55, maxScale: 0.9, - scaleSpeed: 0.2 + scaleSpeed: 0.2, + isCircle: false ) private let bigBlob = BlobView( pointsCount: 8, @@ -38,7 +40,8 @@ final class VoiceBlobView: UIView, TGModernConversationInputMicButtonDecoration maxSpeed: 7, minScale: 0.55, maxScale: 1, - scaleSpeed: 0.2 + scaleSpeed: 0.2, + isCircle: false ) override init(frame: CGRect) { @@ -101,6 +104,9 @@ final class BlobView: UIView { let maxScale: CGFloat let scaleSpeed: CGFloat + // If true ignores randomness and pointsCount + let isCircle: Bool + var level: CGFloat = 0 { didSet { speedLevel = max(level, speedLevel) @@ -153,7 +159,8 @@ final class BlobView: UIView { maxSpeed: CGFloat, minScale: CGFloat, maxScale: CGFloat, - scaleSpeed: CGFloat + scaleSpeed: CGFloat, + isCircle: Bool ) { self.pointsCount = pointsCount self.minRandomness = minRandomness @@ -163,6 +170,7 @@ final class BlobView: UIView { self.minScale = minScale self.maxScale = maxScale self.scaleSpeed = scaleSpeed + self.isCircle = isCircle let angle = (CGFloat.pi * 2) / CGFloat(pointsCount) self.smoothness = ((4 / 3) * tan(angle / 4)) / sin(angle / 2) / 2 @@ -208,6 +216,8 @@ final class BlobView: UIView { } func animateToNewShape() { + guard !isCircle else { return } + if pop_animation(forKey: "blob") != nil { fromPoints = currentPoints toPoints = nil @@ -296,6 +306,13 @@ final class BlobView: UIView { CATransaction.begin() CATransaction.setDisableActions(true) shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY) + if isCircle { + let halfWidth = bounds.width * 0.5 + shapeLayer.path = UIBezierPath( + roundedRect: bounds.offsetBy(dx: -halfWidth, dy: -halfWidth), + cornerRadius: halfWidth + ).cgPath + } CATransaction.commit() } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 36927aa312..1225d33f19 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -376,7 +376,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .inline: navigationBarPresentationData = nil default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: false, hideBadge: false) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) } super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource) @@ -2806,9 +2806,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let navigationBarTheme: NavigationBarTheme if self.hasEmbeddedTitleContent { - navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: true, hideBadge: true) + navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: true) } else { - navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: false, hideBadge: false) + navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) } self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) @@ -8452,6 +8452,58 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private func openUrl(_ url: String, concealed: Bool, message: Message? = nil) { self.commitPurposefulAction() + if self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback { + if url.hasSuffix(".m3u8") { + let navigationController = self.navigationController as? NavigationController + + let webPage = TelegramMediaWebpage( + webpageId: MediaId(namespace: 0, id: 0), + content: .Loaded(TelegramMediaWebpageLoadedContent( + url: url, + displayUrl: url, + hash: 0, + type: "video", + websiteName: nil, + title: nil, + text: nil, + embedUrl: url, + embedType: "video", + embedSize: nil, + duration: nil, + author: nil, + image: nil, + file: nil, + attributes: [], + instantPage: nil + )) + ) + let entry = InstantPageGalleryEntry( + index: 0, + pageId: webPage.webpageId, + media: InstantPageMedia( + index: 0, + media: webPage, + url: nil, + caption: nil, + credit: nil + ), + caption: nil, + credit: nil, + location: nil + ) + + let gallery = InstantPageGalleryController(context: context, webPage: webPage, entries: [entry], centralIndex: 0, replaceRootController: { [weak navigationController] controller, ready in + if let navigationController = navigationController { + navigationController.replaceTopController(controller, animated: false, ready: ready) + } + }, baseNavigationController: navigationController) + self.present(gallery, in: .window(.root), with: InstantPageGalleryControllerPresentationArguments(transitionArguments: { entry -> GalleryTransitionArguments? in + return nil + })) + return; + } + } + openUserGeneratedUrl(context: self.context, url: url, concealed: concealed, present: { [weak self] c in self?.present(c, in: .window(.root)) }, openResolved: { [weak self] resolved in @@ -9387,7 +9439,9 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent func parseUrl(url: String, wasConcealed: Bool) -> (string: String, concealed: Bool) { var parsedUrlValue: URL? - if let parsed = URL(string: url) { + if url.hasPrefix("tel:") { + return (url, false) + } else if let parsed = URL(string: url) { parsedUrlValue = parsed } else if let parsed = URL(string: "https://" + url) { parsedUrlValue = parsed diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 7f26f41e83..5697d193b8 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -563,9 +563,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.addSubnode(self.navigateButtons) self.addSubnode(self.navigationBarBackroundNode) - self.navigationBarBackroundNode.isHidden = true self.addSubnode(self.navigationBarSeparatorNode) - self.navigationBarSeparatorNode.isHidden = true + if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding { + self.navigationBarBackroundNode.isHidden = true + self.navigationBarSeparatorNode.isHidden = true + } self.historyNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) @@ -2691,7 +2693,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } func updateEmbeddedTitlePeekContent(content: NavigationControllerDropContent?) { - return; + if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding { + return + } guard let (_, navigationHeight) = self.validLayout else { return @@ -2719,7 +2723,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var updateHasEmbeddedTitleContent: (() -> Void)? func acceptEmbeddedTitlePeekContent(content: NavigationControllerDropContent) -> Bool { - return false; + if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding { + return false + } guard let (_, navigationHeight) = self.validLayout else { return false diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 668607bcdc..53d7c35a41 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -10,12 +10,34 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { public var knockoutWallpaper: Bool public var foldersTabAtBottom: Bool public var videoCalls: Bool + public var playerEmbedding: Bool + public var playlistPlayback: Bool public static var defaultSettings: ExperimentalUISettings { - return ExperimentalUISettings(keepChatNavigationStack: false, skipReadHistory: false, crashOnLongQueries: false, chatListPhotos: false, knockoutWallpaper: false, foldersTabAtBottom: false, videoCalls: false) + return ExperimentalUISettings( + keepChatNavigationStack: false, + skipReadHistory: false, + crashOnLongQueries: false, + chatListPhotos: false, + knockoutWallpaper: false, + foldersTabAtBottom: false, + videoCalls: false, + playerEmbedding: false, + playlistPlayback: false + ) } - public init(keepChatNavigationStack: Bool, skipReadHistory: Bool, crashOnLongQueries: Bool, chatListPhotos: Bool, knockoutWallpaper: Bool, foldersTabAtBottom: Bool, videoCalls: Bool) { + public init( + keepChatNavigationStack: Bool, + skipReadHistory: Bool, + crashOnLongQueries: Bool, + chatListPhotos: Bool, + knockoutWallpaper: Bool, + foldersTabAtBottom: Bool, + videoCalls: Bool, + playerEmbedding: Bool, + playlistPlayback: Bool + ) { self.keepChatNavigationStack = keepChatNavigationStack self.skipReadHistory = skipReadHistory self.crashOnLongQueries = crashOnLongQueries @@ -23,6 +45,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.knockoutWallpaper = knockoutWallpaper self.foldersTabAtBottom = foldersTabAtBottom self.videoCalls = videoCalls + self.playerEmbedding = playerEmbedding + self.playlistPlayback = playlistPlayback } public init(decoder: PostboxDecoder) { @@ -33,6 +57,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { self.knockoutWallpaper = decoder.decodeInt32ForKey("knockoutWallpaper", orElse: 0) != 0 self.foldersTabAtBottom = decoder.decodeInt32ForKey("foldersTabAtBottom", orElse: 0) != 0 self.videoCalls = decoder.decodeInt32ForKey("videoCalls", orElse: 0) != 0 + self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0 + self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { @@ -43,6 +69,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry { encoder.encodeInt32(self.knockoutWallpaper ? 1 : 0, forKey: "knockoutWallpaper") encoder.encodeInt32(self.foldersTabAtBottom ? 1 : 0, forKey: "foldersTabAtBottom") encoder.encodeInt32(self.videoCalls ? 1 : 0, forKey: "videoCalls") + encoder.encodeInt32(self.playerEmbedding ? 1 : 0, forKey: "playerEmbedding") + encoder.encodeInt32(self.playlistPlayback ? 1 : 0, forKey: "playlistPlayback") } public func isEqual(to: PreferencesEntry) -> Bool { diff --git a/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift index 0688178c82..4f078b082c 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/PlatformVideoContent.swift @@ -46,9 +46,32 @@ public enum PlatformVideoContentId: Hashable { } public final class PlatformVideoContent: UniversalVideoContent { + public enum Content { + case file(FileMediaReference) + case url(String) + + var duration: Int32? { + switch self { + case let .file(file): + return file.media.duration + case .url: + return nil + } + } + + var dimensions: PixelDimensions? { + switch self { + case let .file(file): + return file.media.dimensions + case .url: + return PixelDimensions(width: 480, height: 300) + } + } + } + public let id: AnyHashable let nativeId: PlatformVideoContentId - let fileReference: FileMediaReference + let content: Content public let dimensions: CGSize public let duration: Int32 let streamVideo: Bool @@ -57,12 +80,12 @@ public final class PlatformVideoContent: UniversalVideoContent { let baseRate: Double let fetchAutomatically: Bool - public init(id: PlatformVideoContentId, fileReference: FileMediaReference, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true) { + public init(id: PlatformVideoContentId, content: Content, streamVideo: Bool = false, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true) { self.id = id self.nativeId = id - self.fileReference = fileReference - self.dimensions = fileReference.media.dimensions?.cgSize ?? CGSize(width: 128.0, height: 128.0) - self.duration = fileReference.media.duration ?? 0 + self.content = content + self.dimensions = self.content.dimensions?.cgSize ?? CGSize(width: 480, height: 320) + self.duration = self.content.duration ?? 0 self.streamVideo = streamVideo self.loopVideo = loopVideo self.enableSound = enableSound @@ -71,15 +94,17 @@ public final class PlatformVideoContent: UniversalVideoContent { } public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode { - return PlatformVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically) + return PlatformVideoContentNode(postbox: postbox, audioSessionManager: audioSession, content: self.content, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically) } public func isEqual(to other: UniversalVideoContent) -> Bool { if let other = other as? PlatformVideoContent { if case let .message(_, stableId, _) = self.nativeId { if case .message(_, stableId, _) = other.nativeId { - if self.fileReference.media.isInstantVideo { - return true + if case let .file(file) = self.content { + if file.media.isInstantVideo { + return true + } } } } @@ -90,7 +115,7 @@ public final class PlatformVideoContent: UniversalVideoContent { private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoContentNode { private let postbox: Postbox - private let fileReference: FileMediaReference + private let content: PlatformVideoContent.Content private let approximateDuration: Double private let intrinsicDimensions: CGSize @@ -125,7 +150,7 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte private let imageNode: TransformImageNode - private let playerItem: AVPlayerItem + private var playerItem: AVPlayerItem? private let player: AVPlayer private let playerNode: ASDisplayNode @@ -133,6 +158,9 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte private var statusDisposable: Disposable? private var didPlayToEndTimeObserver: NSObjectProtocol? + private var didBecomeActiveObserver: NSObjectProtocol? + private var willResignActiveObserver: NSObjectProtocol? + private var playerItemFailedToPlayToEndTimeObserver: NSObjectProtocol? private let fetchDisposable = MetaDisposable() @@ -141,16 +169,15 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte private var validLayout: CGSize? - init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool) { + init(postbox: Postbox, audioSessionManager: ManagedAudioSession, content: PlatformVideoContent.Content, streamVideo: Bool, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool) { self.postbox = postbox - self.fileReference = fileReference - self.approximateDuration = Double(fileReference.media.duration ?? 1) + self.content = content + self.approximateDuration = Double(content.duration ?? 1) self.audioSessionManager = audioSessionManager self.imageNode = TransformImageNode() - self.playerItem = AVPlayerItem(url: URL(string: postbox.mediaBox.completedResourcePath(fileReference.media.resource, pathExtension: "mov") ?? "")!) - let player = AVPlayer(playerItem: self.playerItem) + let player = AVPlayer(playerItem: nil) self.player = player self.playerNode = ASDisplayNode() @@ -158,26 +185,31 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte return AVPlayerLayer(player: player) }) - self.intrinsicDimensions = fileReference.media.dimensions?.cgSize ?? CGSize() + self.intrinsicDimensions = content.dimensions?.cgSize ?? CGSize() self.playerNode.frame = CGRect(origin: CGPoint(), size: self.intrinsicDimensions) super.init() - self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference) |> map { [weak self] getSize, getData in - Queue.mainQueue().async { - if let strongSelf = self, strongSelf.dimensions == nil { - if let dimensions = getSize() { - strongSelf.dimensions = dimensions - strongSelf.dimensionsPromise.set(dimensions) - if let size = strongSelf.validLayout { - strongSelf.updateLayout(size: size, transition: .immediate) + switch content { + case let .file(file): + self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: file) |> map { [weak self] getSize, getData in + Queue.mainQueue().async { + if let strongSelf = self, strongSelf.dimensions == nil { + if let dimensions = getSize() { + strongSelf.dimensions = dimensions + strongSelf.dimensionsPromise.set(dimensions) + if let size = strongSelf.validLayout { + strongSelf.updateLayout(size: size, transition: .immediate) + } } } } - } - return getData - }) + return getData + }) + case .url: + break + } self.addSubnode(self.imageNode) self.addSubnode(self.playerNode) @@ -192,18 +224,36 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte } self.player.addObserver(self, forKeyPath: "rate", options: [], context: nil) - playerItem.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil) - playerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil) - playerItem.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil) self._bufferingStatus.set(.single(nil)) + + let playerItem: AVPlayerItem + switch content { + case let .file(file): + playerItem = AVPlayerItem(url: URL(string: postbox.mediaBox.completedResourcePath(file.media.resource, pathExtension: "mov") ?? "")!) + case let .url(url): + playerItem = AVPlayerItem(url: URL(string: url)!) + } + self.setPlayerItem(playerItem) + + self.didBecomeActiveObserver = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: nil, using: { [weak self] _ in + guard let strongSelf = self, let layer = strongSelf.playerNode.layer as? AVPlayerLayer else { + return + } + layer.player = strongSelf.player + }) + self.willResignActiveObserver = NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil, using: { [weak self] _ in + guard let strongSelf = self, let layer = strongSelf.playerNode.layer as? AVPlayerLayer else { + return + } + layer.player = nil + }) } deinit { self.player.removeObserver(self, forKeyPath: "rate") - self.playerItem.removeObserver(self, forKeyPath: "playbackBufferEmpty") - self.playerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") - self.playerItem.removeObserver(self, forKeyPath: "playbackBufferFull") + + self.setPlayerItem(nil) self.audioSessionDisposable.dispose() @@ -213,12 +263,57 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver { NotificationCenter.default.removeObserver(didPlayToEndTimeObserver) } + if let didBecomeActiveObserver = self.didBecomeActiveObserver { + NotificationCenter.default.removeObserver(didBecomeActiveObserver) + } + if let willResignActiveObserver = self.willResignActiveObserver { + NotificationCenter.default.removeObserver(willResignActiveObserver) + } + } + + private func setPlayerItem(_ item: AVPlayerItem?) { + if let playerItem = self.playerItem { + playerItem.removeObserver(self, forKeyPath: "playbackBufferEmpty") + playerItem.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") + playerItem.removeObserver(self, forKeyPath: "playbackBufferFull") + playerItem.removeObserver(self, forKeyPath: "status") + if let playerItemFailedToPlayToEndTimeObserver = self.playerItemFailedToPlayToEndTimeObserver { + NotificationCenter.default.removeObserver(playerItemFailedToPlayToEndTimeObserver) + self.playerItemFailedToPlayToEndTimeObserver = nil + } + } + + self.playerItem = item + + if let playerItem = self.playerItem { + playerItem.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil) + playerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil) + playerItem.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil) + playerItem.addObserver(self, forKeyPath: "status", options: .new, context: nil) + self.playerItemFailedToPlayToEndTimeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, object: playerItem, queue: OperationQueue.main, using: { [weak self] _ in + guard let strongSelf = self else { + return + } + switch strongSelf.content { + case .file: + break + case let .url(url): + let updatedPlayerItem = AVPlayerItem(url: URL(string: url)!) + strongSelf.setPlayerItem(updatedPlayerItem) + } + }) + } + + self.player.replaceCurrentItem(with: self.playerItem) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "rate" { let isPlaying = !self.player.rate.isZero let status: MediaPlayerPlaybackStatus + if isPlaying { + self.isBuffering = false + } if self.isBuffering { status = .buffering(initial: false, whilePlaying: isPlaying) } else { @@ -248,6 +343,21 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte } self.statusValue = MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: status, soundEnabled: true) self._status.set(self.statusValue) + } else if keyPath == "status" { + if let playerItem = self.playerItem, false { + switch playerItem.status { + case .failed: + switch self.content { + case .file: + break + case let .url(url): + let updatedPlayerItem = AVPlayerItem(url: URL(string: url)!) + self.setPlayerItem(updatedPlayerItem) + } + default: + break + } + } } }