Add twitch embed player wrapper

This commit is contained in:
Ilya Laktyushin 2019-11-13 20:24:17 +04:00
parent 401b8c2922
commit 8e60dc4da3
6 changed files with 149 additions and 22 deletions

View File

@ -25,12 +25,8 @@
};
})();
function play() {
invoke("play");
}
function pause() {
invoke("play");
function playPause() {
invoke("playPause");
}
function receiveMessage(evt) {

View File

@ -1,5 +1,5 @@
function initialize() {
var controls = document.getElementsByClassName("player-controls-bottom")[0];
var controls = document.getElementsByClassName("pl-controls-bottom")[0];
if (controls == null) {
controls = document.getElementsByClassName("player-overlay-container")[0];
}
@ -7,9 +7,14 @@ function initialize() {
controls.style.display = "none";
}
var root = document.getElementsByClassName("player-root")[0];
if (root != null) {
root.style.display = "none";
}
var topBar = document.getElementById("top-bar");
if (topBar == null) {
topBar = document.getElementsByClassName("player-controls-top")[0];
topBar = document.getElementsByClassName("pl-controls-top")[0];
}
if (topBar != null) {
topBar.style.display = "none";
@ -17,7 +22,7 @@ function initialize() {
var pauseOverlay = document.getElementsByClassName("player-play-overlay")[0];
if (pauseOverlay == null) {
pauseOverlay = document.getElementsByClassName("player-controls-bottom")[0];
pauseOverlay = document.getElementsByClassName("pl-controls-bottom")[0];
}
if (pauseOverlay != null) {
pauseOverlay.style.display = "none";
@ -85,13 +90,15 @@ function eventFire(el, etype){
}
}
function play() {
function togglePlayPause() {
var playButton = document.getElementsByClassName("js-control-playpause-button")[0];
if (playButton == null) {
playButton = document.getElementsByClassName("player-button--playpause")[0];
playButton = document.getElementsByClassName("player-button")[0];
}
eventFire(playButton, "click");
if (playButton != null) {
eventFire(playButton, "click");
}
}
function receiveMessage(evt) {
@ -105,8 +112,8 @@ function receiveMessage(evt) {
if (obj.command == "initialize")
initialize();
else if (obj.command == "play")
play();
else if (obj.command == "playPause")
togglePlayPause();
} catch (ex) { }
}

View File

@ -0,0 +1,117 @@
import Foundation
import WebKit
import SwiftSignalKit
import UniversalMediaPlayer
import AppBundle
func isTwitchVideoUrl(_ url: String) -> Bool {
return url.contains("//player.twitch.tv/") || url.contains("//clips.twitch.tv/")
}
final class TwitchEmbedImplementation: WebEmbedImplementation {
private var evalImpl: ((String) -> Void)?
private var updateStatus: ((MediaPlayerStatus) -> Void)?
private var onPlaybackStarted: (() -> Void)?
private let url: String
private var status : MediaPlayerStatus
private var started = false
init(url: String) {
self.url = url
self.status = MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .buffering(initial: true, whilePlaying: true), soundEnabled: true)
}
func setup(_ webView: WKWebView, userContentController: WKUserContentController, evaluateJavaScript: @escaping (String) -> Void, updateStatus: @escaping (MediaPlayerStatus) -> Void, onPlaybackStarted: @escaping () -> Void) {
let bundle = getAppBundle()
guard let userScriptPath = bundle.path(forResource: "TwitchUserScript", ofType: "js") else {
return
}
guard let userScriptData = try? Data(contentsOf: URL(fileURLWithPath: userScriptPath)) else {
return
}
guard let userScript = String(data: userScriptData, encoding: .utf8) else {
return
}
guard let htmlTemplatePath = bundle.path(forResource: "Twitch", ofType: "html") else {
return
}
guard let htmlTemplateData = try? Data(contentsOf: URL(fileURLWithPath: htmlTemplatePath)) else {
return
}
guard let htmlTemplate = String(data: htmlTemplateData, encoding: .utf8) else {
return
}
self.evalImpl = evaluateJavaScript
self.updateStatus = updateStatus
self.onPlaybackStarted = onPlaybackStarted
updateStatus(self.status)
let html = String(format: htmlTemplate, self.url)
webView.loadHTMLString(html, baseURL: URL(string: "about:blank"))
userContentController.addUserScript(WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false))
}
func play() {
if let eval = self.evalImpl {
eval("playPause()")
}
self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: self.status.timestamp, baseRate: 1.0, seekId: self.status.seekId, status: .playing, soundEnabled: self.status.soundEnabled)
if let updateStatus = self.updateStatus {
updateStatus(self.status)
}
}
func pause() {
if let eval = self.evalImpl {
eval("playPause()")
}
self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: self.status.timestamp, baseRate: 1.0, seekId: self.status.seekId, status: .paused, soundEnabled: self.status.soundEnabled)
if let updateStatus = self.updateStatus {
updateStatus(self.status)
}
}
func togglePlayPause() {
if self.status.status == .playing {
self.pause()
} else {
self.play()
}
}
func seek(timestamp: Double) {
}
func pageReady() {
// Queue.mainQueue().after(delay: 0.5) {
// if let onPlaybackStarted = self.onPlaybackStarted {
// onPlaybackStarted()
// }
// }
}
func callback(url: URL) {
switch url.host {
case "onPlayback":
if !self.started {
self.started = true
self.status = MediaPlayerStatus(generationTimestamp: self.status.generationTimestamp, duration: self.status.duration, dimensions: self.status.dimensions, timestamp: self.status.timestamp, baseRate: 1.0, seekId: self.status.seekId, status: .playing, soundEnabled: self.status.soundEnabled)
if let updateStatus = self.updateStatus {
updateStatus(self.status)
}
if let onPlaybackStarted = self.onPlaybackStarted {
onPlaybackStarted()
}
}
default:
break
}
}
}

View File

@ -134,7 +134,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
}
func play() {
if let eval = evalImpl {
if let eval = self.evalImpl {
eval("play();")
}
@ -142,7 +142,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
}
func pause() {
if let eval = evalImpl {
if let eval = self.evalImpl {
eval("pause();")
}
}
@ -156,7 +156,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
}
func seek(timestamp: Double) {
if let eval = evalImpl {
if let eval = self.evalImpl {
eval("seek(\(timestamp));")
}
@ -165,7 +165,7 @@ final class VimeoEmbedImplementation: WebEmbedImplementation {
updateStatus(self.status)
}
ignorePosition = 2
self.ignorePosition = 2
}
func pageReady() {

View File

@ -22,6 +22,7 @@ protocol WebEmbedImplementation {
public enum WebEmbedType {
case youtube(videoId: String, timestamp: Int)
case vimeo(videoId: String, timestamp: Int)
case twitch(url: String)
case iframe(url: String)
public var supportsSeeking: Bool {
@ -37,6 +38,10 @@ public enum WebEmbedType {
public func webEmbedType(content: TelegramMediaWebpageLoadedContent, forcedTimestamp: Int? = nil) -> WebEmbedType {
if let (videoId, timestamp) = extractYoutubeVideoIdAndTimestamp(url: content.url) {
return .youtube(videoId: videoId, timestamp: forcedTimestamp ?? timestamp)
} else if let (videoId, timestamp) = extractVimeoVideoIdAndTimestamp(url: content.url) {
return .vimeo(videoId: videoId, timestamp: forcedTimestamp ?? timestamp)
} else if let embedUrl = content.embedUrl, isTwitchVideoUrl(embedUrl) {
return .twitch(url: embedUrl)
} else {
return .iframe(url: content.embedUrl ?? content.url)
}
@ -48,6 +53,8 @@ func webEmbedImplementation(for type: WebEmbedType) -> WebEmbedImplementation {
return YoutubeEmbedImplementation(videoId: videoId, timestamp: timestamp)
case let .vimeo(videoId, timestamp):
return VimeoEmbedImplementation(videoId: videoId, timestamp: timestamp)
case let .twitch(url):
return TwitchEmbedImplementation(url: url)
case let .iframe(url):
return GenericEmbedImplementation(url: url)
}

View File

@ -93,8 +93,8 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
private var ignoreEarlierTimestamps = false
private var status : MediaPlayerStatus
private var ready: Bool = false
private var started: Bool = false
private var ready = false
private var started = false
private var ignorePosition: Int?
private enum PlaybackDelay {
@ -176,7 +176,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
return
}
if let eval = evalImpl {
if let eval = self.evalImpl {
eval("play();")
}
@ -184,7 +184,7 @@ final class YoutubeEmbedImplementation: WebEmbedImplementation {
}
func pause() {
if let eval = evalImpl {
if let eval = self.evalImpl {
eval("pause();")
}
}