diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index b2e443b830..592120b113 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -21,6 +21,7 @@ import TextSelectionNode import UrlEscaping import UndoUI import ManagedAnimationNode +import TelegramUniversalVideoContent private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white) private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white) @@ -593,6 +594,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll break } } + } else if let media = media as? TelegramMediaWebpage, case let .Loaded(content) = media.content { + let type = webEmbedType(content: content) + switch type { + case .youtube, .vimeo: + canFullscreen = true + default: + break + } } } diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 0758cffc8e..980337b2a2 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -774,6 +774,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { var forceEnablePiP = false var forceEnableUserInteraction = false var isAnimated = false + var isEnhancedWebPlayer = false if let content = item.content as? NativeVideoContent { isAnimated = content.fileReference.media.isAnimated self.videoFramePreview = MediaPlayerFramePreview(postbox: item.context.account.postbox, fileReference: content.fileReference) @@ -783,9 +784,12 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { let type = webEmbedType(content: content.webpageContent) switch type { case .youtube: + isEnhancedWebPlayer = true forceEnableUserInteraction = true disablePictureInPicture = !(item.configuration?.youtubePictureInPictureEnabled ?? false) self.videoFramePreview = YoutubeEmbedFramePreview(context: item.context, content: content) + case .vimeo: + isEnhancedWebPlayer = true case .iframe: disablePlayerControls = true default: @@ -1122,7 +1126,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } } - if !isWebpage, let file = file, !file.isAnimated { + var hasMoreButton = false + if isEnhancedWebPlayer { + hasMoreButton = true + } else if !isWebpage, let file = file, !file.isAnimated { + hasMoreButton = true + } + + if hasMoreButton { let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)! barButtonItems.append(moreMenuItem) } @@ -1316,6 +1327,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let time = item.timecode { seek = .timecode(time) } + playbackRate = item.playbackRate } } videoNode.setBaseRate(playbackRate) @@ -1922,7 +1934,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { } } - private func contentInfo() -> (message: Message, file: TelegramMediaFile, isWebpage: Bool)? { + private func contentInfo() -> (message: Message, file: TelegramMediaFile?, isWebpage: Bool)? { guard let item = self.item else { return nil } @@ -1933,16 +1945,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let m = m as? TelegramMediaFile, m.isVideo { file = m break - } else if let m = m as? TelegramMediaWebpage, case let .Loaded(content) = m.content, let f = content.file, f.isVideo { - file = f + } else if let m = m as? TelegramMediaWebpage, case let .Loaded(content) = m.content { + if let f = content.file, f.isVideo { + file = f + } isWebpage = true break } } - - if let file = file { - return (message, file, isWebpage) - } + return (message, file, isWebpage) } return nil } @@ -2041,7 +2052,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { c.setItems(strongSelf.contextMenuSpeedItems()) }))) - if let (message, file, isWebpage) = strongSelf.contentInfo(), !isWebpage { + if let (message, maybeFile, isWebpage) = strongSelf.contentInfo(), let file = maybeFile, !isWebpage { items.append(.action(ContextMenuActionItem(text: "Save to Gallery", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in f(.default) diff --git a/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m b/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m index 274e33e4be..3070c7cb47 100644 --- a/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m +++ b/submodules/LegacyComponents/Sources/TGCameraMainPhoneView.m @@ -282,17 +282,7 @@ } completion:nil]; }; [self addSubview:_zoomView]; - - _flashControl.becameActive = ^ - { - __strong TGCameraMainPhoneView *strongSelf = weakSelf; - if (strongSelf == nil) - return; - if (strongSelf->_modeControl.cameraMode == PGCameraModeVideo) - [strongSelf->_timecodeView setHidden:true animated:true]; - }; - _flashControl.modeChanged = ^(PGCameraFlashMode mode) { __strong TGCameraMainPhoneView *strongSelf = weakSelf; @@ -301,9 +291,6 @@ if (strongSelf.flashModeChanged != nil) strongSelf.flashModeChanged(mode); - - if (strongSelf->_modeControl.cameraMode == PGCameraModeVideo) - [strongSelf->_timecodeView setHidden:false animated:true]; }; _modeControl.modeChanged = ^(PGCameraMode mode, PGCameraMode previousMode) diff --git a/submodules/TelegramUI/Resources/WebEmbed/Vimeo.html b/submodules/TelegramUI/Resources/WebEmbed/Vimeo.html index ecea52af89..22f609f06c 100755 --- a/submodules/TelegramUI/Resources/WebEmbed/Vimeo.html +++ b/submodules/TelegramUI/Resources/WebEmbed/Vimeo.html @@ -9,14 +9,10 @@
- +
+ diff --git a/submodules/TelegramUI/Resources/WebEmbed/VimeoUserScript.js b/submodules/TelegramUI/Resources/WebEmbed/VimeoUserScript.js index daba76762d..02620c3a81 100644 --- a/submodules/TelegramUI/Resources/WebEmbed/VimeoUserScript.js +++ b/submodules/TelegramUI/Resources/WebEmbed/VimeoUserScript.js @@ -4,17 +4,17 @@ function initialize() { controls.style.display = "none"; } - var sidedock = document.getElementsByClassName("sidedock")[0]; + var sidedock = document.getElementsByClassName("vp-sidedock")[0]; if (sidedock != null) { sidedock.style.display = "none"; } - var video = document.getElementsByTagName("video")[0]; - if (video != null) { - video.setAttribute("webkit-playsinline", ""); - video.setAttribute("playsinline", ""); - video.webkitEnterFullscreen = undefined; - } +// var video = document.getElementsByTagName("video")[0]; +// if (video != null) { +// video.setAttribute("webkit-playsinline", ""); +// video.setAttribute("playsinline", ""); +// video.webkitEnterFullscreen = undefined; +// } } function eventFire(el, etype){ diff --git a/submodules/TelegramUI/Resources/WebEmbed/Youtube.html b/submodules/TelegramUI/Resources/WebEmbed/Youtube.html index 8e3f1b5096..a18ee62bd6 100755 --- a/submodules/TelegramUI/Resources/WebEmbed/Youtube.html +++ b/submodules/TelegramUI/Resources/WebEmbed/Youtube.html @@ -60,6 +60,10 @@ player.seekTo(timestamp, true); } + function setRate(rate) { + player.setPlaybackRate(rate); + } + function updateState() { window.location.href = "embed://onState?failed=" + failed + "&playback=" + playbackState + "&position=" + position + "&duration=" + duration + "&download=" + downloadProgress + '&quality=' + quality + '&availableQualities=' + availableQualities + '&storyboard=' + storyboardSpec; } diff --git a/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift index 731c1364b5..c525db9e45 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/GenericEmbedImplementation.swift @@ -71,6 +71,9 @@ final class GenericEmbedImplementation: WebEmbedImplementation { func seek(timestamp: Double) { } + func setBaseRate(_ baseRate: Double) { + } + func pageReady() { self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .playing, soundEnabled: true) self.updateStatus?(self.status) diff --git a/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift index 85fb56e6ba..58fadf788c 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/TwitchEmbedImplementation.swift @@ -88,6 +88,9 @@ final class TwitchEmbedImplementation: WebEmbedImplementation { func seek(timestamp: Double) { } + func setBaseRate(_ baseRate: Double) { + } + func pageReady() { // Queue.mainQueue().after(delay: 0.5) { // if let onPlaybackStarted = self.onPlaybackStarted { diff --git a/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift index 453208b145..f5b911b5a0 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/VimeoEmbedImplementation.swift @@ -88,6 +88,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation { private let videoId: String private let timestamp: Int + private var baseRate: Double = 1.0 private var status : MediaPlayerStatus private var ready: Bool = false @@ -160,12 +161,30 @@ final class VimeoEmbedImplementation: WebEmbedImplementation { eval("seek(\(timestamp));", nil) } - self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: timestamp, baseRate: 1.0, seekId: self.status.seekId + 1, status: self.status.status, soundEnabled: self.status.soundEnabled) - if let updateStatus = self.updateStatus { - updateStatus(self.status) + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: timestamp, baseRate: self.status.baseRate, seekId: self.status.seekId + 1, status: self.status.status, soundEnabled: self.status.soundEnabled) + self.updateStatus?(self.status) + + self.ignorePosition = 2 + } + + func setBaseRate(_ baseRate: Double) { + var baseRate = baseRate + if baseRate < 0.5 { + baseRate = 0.5 + } + if baseRate > 2.0 { + baseRate = 2.0 + } + if !self.ready { + self.baseRate = baseRate } - self.ignorePosition = 2 + if let eval = self.evalImpl { + eval("setRate(\(baseRate));", nil) + } + + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: self.status.timestamp, baseRate: baseRate, seekId: self.status.seekId + 1, status: self.status.status, soundEnabled: true) + self.updateStatus?(self.status) } func pageReady() { @@ -210,6 +229,11 @@ final class VimeoEmbedImplementation: WebEmbedImplementation { } } + if !self.ready { + self.ready = true + self.play() + } + if let updateStatus = self.updateStatus, let playback = playback, let duration = duration { let playbackStatus: MediaPlayerPlaybackStatus switch playback { @@ -226,10 +250,12 @@ final class VimeoEmbedImplementation: WebEmbedImplementation { if case .playing = playbackStatus, !self.started { self.started = true - self.onPlaybackStarted?() + Queue.mainQueue().after(0.5) { + self.onPlaybackStarted?() + } } - self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: 1.0, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true) + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: self.status.baseRate, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true) updateStatus(self.status) } } diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift index 2de523f71a..682283a278 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedPlayerNode.swift @@ -14,6 +14,7 @@ protocol WebEmbedImplementation { func pause() func togglePlayPause() func seek(timestamp: Double) + func setBaseRate(_ baseRate: Double) func pageReady() func callback(url: URL) @@ -170,6 +171,10 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate { self.impl.seek(timestamp: timestamp) } + func setBaseRate(_ baseRate: Double) { + self.impl.setBaseRate(baseRate) + } + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { } diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift index d5aa2686a0..a7dfd3c409 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/WebEmbedVideoContent.swift @@ -172,6 +172,7 @@ final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode { } func setBaseRate(_ baseRate: Double) { + self.playerNode.setBaseRate(baseRate) } func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int { diff --git a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift index 20affc0cb1..eb01468635 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/YoutubeEmbedImplementation.swift @@ -100,6 +100,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { } private var timestamp: Int + private var baseRate: Double = 1.0 private var ignoreEarlierTimestamps = false private var status: MediaPlayerStatus @@ -107,6 +108,8 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { private var started = false private var ignorePosition: Int? + private var isPlaying = true + private enum PlaybackDelay { case none case afterPositionUpdates(count: Int) @@ -186,6 +189,8 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { return } + self.isPlaying = true + if let eval = self.evalImpl { eval("play();", nil) } @@ -194,6 +199,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { } func pause() { + self.isPlaying = false if let eval = self.evalImpl { eval("pause();", nil) } @@ -201,9 +207,9 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { func togglePlayPause() { if case .playing = self.status.status { - pause() + self.pause() } else { - play() + self.play() } } @@ -217,12 +223,32 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { eval("seek(\(timestamp));", nil) } - self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: timestamp, baseRate: 1.0, seekId: self.status.seekId + 1, status: self.status.status, soundEnabled: true) + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: timestamp, baseRate: self.status.baseRate, seekId: self.status.seekId + 1, status: self.status.status, soundEnabled: true) self.updateStatus?(self.status) self.ignorePosition = 2 } + func setBaseRate(_ baseRate: Double) { + var baseRate = baseRate + if baseRate < 0.5 { + baseRate = 0.5 + } + if baseRate > 2.0 { + baseRate = 2.0 + } + if !self.ready { + self.baseRate = baseRate + } + + if let eval = self.evalImpl { + eval("setRate(\(baseRate));", nil) + } + + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: self.status.timestamp, baseRate: baseRate, seekId: self.status.seekId + 1, status: self.status.status, soundEnabled: true) + self.updateStatus?(self.status) + } + func pageReady() { } @@ -283,6 +309,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { switch playback { case 0: if newTimestamp > Double(duration) - 1.0 { + self.isPlaying = false playbackStatus = .paused newTimestamp = 0.0 } else { @@ -293,9 +320,9 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { case 2: playbackStatus = .paused case 3: - playbackStatus = .buffering(initial: false, whilePlaying: true, progress: 0.0, display: false) + playbackStatus = .buffering(initial: !self.started, whilePlaying: self.isPlaying, progress: 0.0, display: false) default: - playbackStatus = .buffering(initial: true, whilePlaying: false, progress: 0.0, display: false) + playbackStatus = .buffering(initial: true, whilePlaying: true, progress: 0.0, display: false) } if case .playing = playbackStatus, !self.started { @@ -305,7 +332,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { self.onPlaybackStarted?() } - self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: 1.0, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true) + self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: Double(duration), dimensions: self.status.dimensions, timestamp: newTimestamp, baseRate: self.status.baseRate, seekId: self.status.seekId, status: playbackStatus, soundEnabled: true) updateStatus(self.status) } } @@ -327,12 +354,20 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation { self.play() } + if self.baseRate != 1.0 { + self.setBaseRate(self.baseRate) + } + print("YT ready in \(CFAbsoluteTimeGetCurrent() - self.benchmarkStartTime)") Queue.mainQueue().async { - self.play() - let delay = self.timestamp > 0 ? 2.8 : 2.0 + if self.timestamp > 0 { + self.seek(timestamp: Double(self.timestamp)) + self.play() + } else { + self.play() + } Queue.mainQueue().after(delay, { if !self.started { self.play()