mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add playback rate control for Youtube & Vimeo
This commit is contained in:
parent
4433eefe15
commit
8c69553891
@ -21,6 +21,7 @@ import TextSelectionNode
|
|||||||
import UrlEscaping
|
import UrlEscaping
|
||||||
import UndoUI
|
import UndoUI
|
||||||
import ManagedAnimationNode
|
import ManagedAnimationNode
|
||||||
|
import TelegramUniversalVideoContent
|
||||||
|
|
||||||
private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white)
|
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)
|
private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white)
|
||||||
@ -593,6 +594,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
break
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,6 +774,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
var forceEnablePiP = false
|
var forceEnablePiP = false
|
||||||
var forceEnableUserInteraction = false
|
var forceEnableUserInteraction = false
|
||||||
var isAnimated = false
|
var isAnimated = false
|
||||||
|
var isEnhancedWebPlayer = false
|
||||||
if let content = item.content as? NativeVideoContent {
|
if let content = item.content as? NativeVideoContent {
|
||||||
isAnimated = content.fileReference.media.isAnimated
|
isAnimated = content.fileReference.media.isAnimated
|
||||||
self.videoFramePreview = MediaPlayerFramePreview(postbox: item.context.account.postbox, fileReference: content.fileReference)
|
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)
|
let type = webEmbedType(content: content.webpageContent)
|
||||||
switch type {
|
switch type {
|
||||||
case .youtube:
|
case .youtube:
|
||||||
|
isEnhancedWebPlayer = true
|
||||||
forceEnableUserInteraction = true
|
forceEnableUserInteraction = true
|
||||||
disablePictureInPicture = !(item.configuration?.youtubePictureInPictureEnabled ?? false)
|
disablePictureInPicture = !(item.configuration?.youtubePictureInPictureEnabled ?? false)
|
||||||
self.videoFramePreview = YoutubeEmbedFramePreview(context: item.context, content: content)
|
self.videoFramePreview = YoutubeEmbedFramePreview(context: item.context, content: content)
|
||||||
|
case .vimeo:
|
||||||
|
isEnhancedWebPlayer = true
|
||||||
case .iframe:
|
case .iframe:
|
||||||
disablePlayerControls = true
|
disablePlayerControls = true
|
||||||
default:
|
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)!
|
let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)!
|
||||||
barButtonItems.append(moreMenuItem)
|
barButtonItems.append(moreMenuItem)
|
||||||
}
|
}
|
||||||
@ -1316,6 +1327,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
if let time = item.timecode {
|
if let time = item.timecode {
|
||||||
seek = .timecode(time)
|
seek = .timecode(time)
|
||||||
}
|
}
|
||||||
|
playbackRate = item.playbackRate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
videoNode.setBaseRate(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 {
|
guard let item = self.item else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1933,16 +1945,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
if let m = m as? TelegramMediaFile, m.isVideo {
|
if let m = m as? TelegramMediaFile, m.isVideo {
|
||||||
file = m
|
file = m
|
||||||
break
|
break
|
||||||
} else if let m = m as? TelegramMediaWebpage, case let .Loaded(content) = m.content, let f = content.file, f.isVideo {
|
} else if let m = m as? TelegramMediaWebpage, case let .Loaded(content) = m.content {
|
||||||
file = f
|
if let f = content.file, f.isVideo {
|
||||||
|
file = f
|
||||||
|
}
|
||||||
isWebpage = true
|
isWebpage = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return (message, file, isWebpage)
|
||||||
if let file = file {
|
|
||||||
return (message, file, isWebpage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2041,7 +2052,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
|
|
||||||
c.setItems(strongSelf.contextMenuSpeedItems())
|
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
|
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)
|
f(.default)
|
||||||
|
|
||||||
|
@ -282,17 +282,7 @@
|
|||||||
} completion:nil];
|
} completion:nil];
|
||||||
};
|
};
|
||||||
[self addSubview:_zoomView];
|
[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)
|
_flashControl.modeChanged = ^(PGCameraFlashMode mode)
|
||||||
{
|
{
|
||||||
__strong TGCameraMainPhoneView *strongSelf = weakSelf;
|
__strong TGCameraMainPhoneView *strongSelf = weakSelf;
|
||||||
@ -301,9 +291,6 @@
|
|||||||
|
|
||||||
if (strongSelf.flashModeChanged != nil)
|
if (strongSelf.flashModeChanged != nil)
|
||||||
strongSelf.flashModeChanged(mode);
|
strongSelf.flashModeChanged(mode);
|
||||||
|
|
||||||
if (strongSelf->_modeControl.cameraMode == PGCameraModeVideo)
|
|
||||||
[strongSelf->_timecodeView setHidden:false animated:true];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_modeControl.modeChanged = ^(PGCameraMode mode, PGCameraMode previousMode)
|
_modeControl.modeChanged = ^(PGCameraMode mode, PGCameraMode previousMode)
|
||||||
|
@ -9,14 +9,10 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<iframe id="player" src="https://player.vimeo.com/video/%@?api=1&badge=0&byline=0&portrait=0&title=0&player_id=player" width="100%" height="100%" frameborder="0"></iframe>
|
<iframe id="player" src="https://player.vimeo.com/video/%@?badge=0&byline=0&portrait=0&title=0" width="100%" height="100%" frameborder="0"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="https://player.vimeo.com/api/player.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var Froogaloop=function(){function e(a){return new e.fn.init(a)}function g(a,c,b){if(!b.contentWindow.postMessage)return!1;a=JSON.stringify({method:a,value:c});b.contentWindow.postMessage(a,h)}function l(a){var c,b;try{c=JSON.parse(a.data),b=c.event||c.method}catch(e){}"ready"!=b||k||(k=!0);if(!/^https?:\/\/player.vimeo.com/.test(a.origin))return!1;"*"===h&&(h=a.origin);a=c.value;var m=c.data,f=""===f?null:c.player_id;c=f?d[f][b]:d[b];b=[];if(!c)return!1;void 0!==a&&b.push(a);m&&b.push(m);f&&b.push(f);
|
|
||||||
return 0<b.length?c.apply(null,b):c.call()}function n(a,c,b){b?(d[b]||(d[b]={}),d[b][a]=c):d[a]=c}var d={},k=!1,h="*";e.fn=e.prototype={element:null,init:function(a){"string"===typeof a&&(a=document.getElementById(a));this.element=a;return this},api:function(a,c){if(!this.element||!a)return!1;var b=this.element,d=""!==b.id?b.id:null,e=c&&c.constructor&&c.call&&c.apply?null:c,f=c&&c.constructor&&c.call&&c.apply?c:null;f&&n(a,f,d);g(a,e,b);return this},addEvent:function(a,c){if(!this.element)return!1;
|
|
||||||
var b=this.element,d=""!==b.id?b.id:null;n(a,c,d);"ready"!=a?g("addEventListener",a,b):"ready"==a&&k&&c.call(null,d);return this},removeEvent:function(a){if(!this.element)return!1;var c=this.element,b=""!==c.id?c.id:null;a:{if(b&&d[b]){if(!d[b][a]){b=!1;break a}d[b][a]=null}else{if(!d[a]){b=!1;break a}d[a]=null}b=!0}"ready"!=a&&b&&g("removeEventListener",a,c)}};e.fn.init.prototype=e.fn;window.addEventListener?window.addEventListener("message",l,!1):window.attachEvent("onmessage",l);return window.Froogaloop=
|
|
||||||
window.$f=e}();
|
|
||||||
|
|
||||||
var iframe;
|
var iframe;
|
||||||
var player;
|
var player;
|
||||||
function invoke(command) {
|
function invoke(command) {
|
||||||
@ -26,7 +22,7 @@
|
|||||||
var played = false;
|
var played = false;
|
||||||
function play() {
|
function play() {
|
||||||
if (played) {
|
if (played) {
|
||||||
player.api("play");
|
player.play();
|
||||||
} else {
|
} else {
|
||||||
invoke("autoplay");
|
invoke("autoplay");
|
||||||
played = true;
|
played = true;
|
||||||
@ -34,44 +30,46 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pause() {
|
function pause() {
|
||||||
player.api("pause");
|
player.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
function seek(timestamp) {
|
function seek(timestamp) {
|
||||||
player.api("seekTo", timestamp);
|
player.setCurrentTime(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRate(rate) {
|
||||||
|
player.setPlaybackRate(rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var playbackState = 0;
|
var playbackState = 1;
|
||||||
var duration = 0.0;
|
var duration = 0.0;
|
||||||
var position = 0.0;
|
var position = 0.0;
|
||||||
var downloadProgress = 0.0;
|
var downloadProgress = 0.0;
|
||||||
|
|
||||||
iframe = document.querySelectorAll("iframe")[0];
|
iframe = document.querySelectorAll("iframe")[0];
|
||||||
player = $f(iframe);
|
player = new Vimeo.Player(iframe);
|
||||||
|
player.getCurrentTime().then(function(seconds) {
|
||||||
function updateState() {
|
position = seconds;
|
||||||
window.location.href = "embed://onState?playback=" + playbackState + "&position=" + position + "&duration=" + duration + "&download=" + downloadProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
player.addEvent("ready", function(player_id) {
|
|
||||||
window.location.href = "embed://onReady?data=" + player_id;
|
|
||||||
|
|
||||||
player.addEvent("play", onPlay);
|
|
||||||
player.addEvent("pause", onPause);
|
|
||||||
player.addEvent("finish", onFinish);
|
|
||||||
player.addEvent("playProgress", onPlayProgress);
|
|
||||||
player.addEvent("loadProgress", onLoadProgress);
|
|
||||||
|
|
||||||
window.setInterval(updateState, 500);
|
|
||||||
|
|
||||||
invoke("initialize");
|
|
||||||
|
|
||||||
if (%@) {
|
|
||||||
invoke("autoplay");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
player.getDuration().then(function(seconds) {
|
||||||
|
duration = seconds;
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateState() {
|
||||||
|
player.getPaused().then(function(paused) {
|
||||||
|
playbackState = paused ? 0 : 1;
|
||||||
|
});
|
||||||
|
player.getCurrentTime().then(function(seconds) {
|
||||||
|
position = seconds;
|
||||||
|
});
|
||||||
|
player.getDuration().then(function(seconds) {
|
||||||
|
duration = seconds;
|
||||||
|
});
|
||||||
|
window.location.href = "embed://onState?playback=" + playbackState + "&position=" + position + "&duration=" + duration + "&download=" + downloadProgress;
|
||||||
|
invoke("initialize");
|
||||||
|
}
|
||||||
|
|
||||||
function onPlay(data) {
|
function onPlay(data) {
|
||||||
playbackState = 1;
|
playbackState = 1;
|
||||||
updateState();
|
updateState();
|
||||||
@ -95,6 +93,16 @@
|
|||||||
function onLoadProgress(data) {
|
function onLoadProgress(data) {
|
||||||
downloadProgress = data.percent;
|
downloadProgress = data.percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player.on('play', onPlay);
|
||||||
|
player.on('pause', onPause);
|
||||||
|
player.on("ended", onFinish);
|
||||||
|
|
||||||
|
window.setInterval(updateState, 500);
|
||||||
|
|
||||||
|
if (%@) {
|
||||||
|
invoke("autoplay");
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -4,17 +4,17 @@ function initialize() {
|
|||||||
controls.style.display = "none";
|
controls.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
var sidedock = document.getElementsByClassName("sidedock")[0];
|
var sidedock = document.getElementsByClassName("vp-sidedock")[0];
|
||||||
if (sidedock != null) {
|
if (sidedock != null) {
|
||||||
sidedock.style.display = "none";
|
sidedock.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
var video = document.getElementsByTagName("video")[0];
|
// var video = document.getElementsByTagName("video")[0];
|
||||||
if (video != null) {
|
// if (video != null) {
|
||||||
video.setAttribute("webkit-playsinline", "");
|
// video.setAttribute("webkit-playsinline", "");
|
||||||
video.setAttribute("playsinline", "");
|
// video.setAttribute("playsinline", "");
|
||||||
video.webkitEnterFullscreen = undefined;
|
// video.webkitEnterFullscreen = undefined;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventFire(el, etype){
|
function eventFire(el, etype){
|
||||||
|
@ -60,6 +60,10 @@
|
|||||||
player.seekTo(timestamp, true);
|
player.seekTo(timestamp, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setRate(rate) {
|
||||||
|
player.setPlaybackRate(rate);
|
||||||
|
}
|
||||||
|
|
||||||
function updateState() {
|
function updateState() {
|
||||||
window.location.href = "embed://onState?failed=" + failed + "&playback=" + playbackState + "&position=" + position + "&duration=" + duration + "&download=" + downloadProgress + '&quality=' + quality + '&availableQualities=' + availableQualities + '&storyboard=' + storyboardSpec;
|
window.location.href = "embed://onState?failed=" + failed + "&playback=" + playbackState + "&position=" + position + "&duration=" + duration + "&download=" + downloadProgress + '&quality=' + quality + '&availableQualities=' + availableQualities + '&storyboard=' + storyboardSpec;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,9 @@ final class GenericEmbedImplementation: WebEmbedImplementation {
|
|||||||
func seek(timestamp: Double) {
|
func seek(timestamp: Double) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setBaseRate(_ baseRate: Double) {
|
||||||
|
}
|
||||||
|
|
||||||
func pageReady() {
|
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.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)
|
self.updateStatus?(self.status)
|
||||||
|
@ -88,6 +88,9 @@ final class TwitchEmbedImplementation: WebEmbedImplementation {
|
|||||||
func seek(timestamp: Double) {
|
func seek(timestamp: Double) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setBaseRate(_ baseRate: Double) {
|
||||||
|
}
|
||||||
|
|
||||||
func pageReady() {
|
func pageReady() {
|
||||||
// Queue.mainQueue().after(delay: 0.5) {
|
// Queue.mainQueue().after(delay: 0.5) {
|
||||||
// if let onPlaybackStarted = self.onPlaybackStarted {
|
// if let onPlaybackStarted = self.onPlaybackStarted {
|
||||||
|
@ -88,6 +88,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
|
|||||||
|
|
||||||
private let videoId: String
|
private let videoId: String
|
||||||
private let timestamp: Int
|
private let timestamp: Int
|
||||||
|
private var baseRate: Double = 1.0
|
||||||
private var status : MediaPlayerStatus
|
private var status : MediaPlayerStatus
|
||||||
|
|
||||||
private var ready: Bool = false
|
private var ready: Bool = false
|
||||||
@ -160,12 +161,30 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
|
|||||||
eval("seek(\(timestamp));", nil)
|
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)
|
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)
|
||||||
if let updateStatus = self.updateStatus {
|
self.updateStatus?(self.status)
|
||||||
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() {
|
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 {
|
if let updateStatus = self.updateStatus, let playback = playback, let duration = duration {
|
||||||
let playbackStatus: MediaPlayerPlaybackStatus
|
let playbackStatus: MediaPlayerPlaybackStatus
|
||||||
switch playback {
|
switch playback {
|
||||||
@ -226,10 +250,12 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
|
|||||||
|
|
||||||
if case .playing = playbackStatus, !self.started {
|
if case .playing = playbackStatus, !self.started {
|
||||||
self.started = true
|
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)
|
updateStatus(self.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ protocol WebEmbedImplementation {
|
|||||||
func pause()
|
func pause()
|
||||||
func togglePlayPause()
|
func togglePlayPause()
|
||||||
func seek(timestamp: Double)
|
func seek(timestamp: Double)
|
||||||
|
func setBaseRate(_ baseRate: Double)
|
||||||
|
|
||||||
func pageReady()
|
func pageReady()
|
||||||
func callback(url: URL)
|
func callback(url: URL)
|
||||||
@ -170,6 +171,10 @@ final class WebEmbedPlayerNode: ASDisplayNode, WKNavigationDelegate {
|
|||||||
self.impl.seek(timestamp: timestamp)
|
self.impl.seek(timestamp: timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setBaseRate(_ baseRate: Double) {
|
||||||
|
self.impl.setBaseRate(baseRate)
|
||||||
|
}
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,7 @@ final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setBaseRate(_ baseRate: Double) {
|
func setBaseRate(_ baseRate: Double) {
|
||||||
|
self.playerNode.setBaseRate(baseRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
func addPlaybackCompleted(_ f: @escaping () -> Void) -> Int {
|
||||||
|
@ -100,6 +100,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var timestamp: Int
|
private var timestamp: Int
|
||||||
|
private var baseRate: Double = 1.0
|
||||||
private var ignoreEarlierTimestamps = false
|
private var ignoreEarlierTimestamps = false
|
||||||
private var status: MediaPlayerStatus
|
private var status: MediaPlayerStatus
|
||||||
|
|
||||||
@ -107,6 +108,8 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
private var started = false
|
private var started = false
|
||||||
private var ignorePosition: Int?
|
private var ignorePosition: Int?
|
||||||
|
|
||||||
|
private var isPlaying = true
|
||||||
|
|
||||||
private enum PlaybackDelay {
|
private enum PlaybackDelay {
|
||||||
case none
|
case none
|
||||||
case afterPositionUpdates(count: Int)
|
case afterPositionUpdates(count: Int)
|
||||||
@ -186,6 +189,8 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.isPlaying = true
|
||||||
|
|
||||||
if let eval = self.evalImpl {
|
if let eval = self.evalImpl {
|
||||||
eval("play();", nil)
|
eval("play();", nil)
|
||||||
}
|
}
|
||||||
@ -194,6 +199,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pause() {
|
func pause() {
|
||||||
|
self.isPlaying = false
|
||||||
if let eval = self.evalImpl {
|
if let eval = self.evalImpl {
|
||||||
eval("pause();", nil)
|
eval("pause();", nil)
|
||||||
}
|
}
|
||||||
@ -201,9 +207,9 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
|
|
||||||
func togglePlayPause() {
|
func togglePlayPause() {
|
||||||
if case .playing = self.status.status {
|
if case .playing = self.status.status {
|
||||||
pause()
|
self.pause()
|
||||||
} else {
|
} else {
|
||||||
play()
|
self.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,12 +223,32 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
eval("seek(\(timestamp));", nil)
|
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.updateStatus?(self.status)
|
||||||
|
|
||||||
self.ignorePosition = 2
|
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() {
|
func pageReady() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,6 +309,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
switch playback {
|
switch playback {
|
||||||
case 0:
|
case 0:
|
||||||
if newTimestamp > Double(duration) - 1.0 {
|
if newTimestamp > Double(duration) - 1.0 {
|
||||||
|
self.isPlaying = false
|
||||||
playbackStatus = .paused
|
playbackStatus = .paused
|
||||||
newTimestamp = 0.0
|
newTimestamp = 0.0
|
||||||
} else {
|
} else {
|
||||||
@ -293,9 +320,9 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
case 2:
|
case 2:
|
||||||
playbackStatus = .paused
|
playbackStatus = .paused
|
||||||
case 3:
|
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:
|
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 {
|
if case .playing = playbackStatus, !self.started {
|
||||||
@ -305,7 +332,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
self.onPlaybackStarted?()
|
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)
|
updateStatus(self.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,12 +354,20 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
|
|||||||
self.play()
|
self.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.baseRate != 1.0 {
|
||||||
|
self.setBaseRate(self.baseRate)
|
||||||
|
}
|
||||||
|
|
||||||
print("YT ready in \(CFAbsoluteTimeGetCurrent() - self.benchmarkStartTime)")
|
print("YT ready in \(CFAbsoluteTimeGetCurrent() - self.benchmarkStartTime)")
|
||||||
|
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
self.play()
|
|
||||||
|
|
||||||
let delay = self.timestamp > 0 ? 2.8 : 2.0
|
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, {
|
Queue.mainQueue().after(delay, {
|
||||||
if !self.started {
|
if !self.started {
|
||||||
self.play()
|
self.play()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user