如何检测正在WKWebView中播放的电影? [英] How to detect a movie being played in a WKWebView?

查看:1561
本文介绍了如何检测正在WKWebView中播放的电影?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以检测到在WKWebView中播放的电影?

I'd like to know wether it's possible to detect a movie being played in the WKWebView?

另外我想知道打开的确切网址流?

Additionally I'd like to know the exact URL of the opened stream?

推荐答案

由于这个问题的解决方案需要大量的研究和不同的方法,我想记录下来,供其他人遵循我的想法。 如果您只是对最终解决方案感兴趣,请寻找一些精美的标题。

Since the solution(s) to this question required a lot of research and different approaches, I'd like to document it here for others to follow my thoughts. If you're just interested in the final solution, look for some fancy headings.

我开始使用的应用程序非常简单。这是一个单视图应用程序导入 WebKit 并打开一个 WKWebView ,其中包含一些 NSURL

The app I started with, was pretty simple. It's a Single-View Application that imports WebKit and opens a WKWebView with some NSURL:

import UIKit
import WebKit
class ViewController: UIViewController {
    var webView: WKWebView!

    override func viewDidAppear(animated: Bool) {
        webView = WKWebView()
        view = webView
        let request = NSURLRequest(URL: NSURL(string: "http://tinas-burger.tumblr.com/post/133991473113")!)
        webView.loadRequest(request) 
    }
}




该URL包含一个受JavaScript保护的视频。 我还没看过这个视频,这只是我发现的第一个。请记住添加 NSAppTransportSecurity NSAllowsArbitraryLoads 到你的 Info.plist ,否则你会看到一个空白页面。

The URL includes a video that is (kind of) protected by JavaScript. I really haven't seen the video yet, it was just the first I discovered. Remember to add NSAppTransportSecurity and NSAllowsArbitraryLoads to your Info.plist or you will see a blank page.



WKNavigationDelegate



WKNavigationDelegate 不会通知您正在播放的视频。因此,设置 webView.navigationDelegate = self 并实施协议将无法为您带来所需的结果。

WKNavigationDelegate

The WKNavigationDelegate won't notify you about a video being played. So setting webView.navigationDelegate = self and implementing the protocol won't bring you the desired results.

我认为必须有像 SomeVideoPlayerDidOpen 这样的事件。不幸的是没有,但它可能有一个 SomeViewDidOpen 事件,所以我开始检查视图层次结构:

I assumed that there must be an event like SomeVideoPlayerDidOpen. Unfortunately there wasn't any, but it might have a SomeViewDidOpen event, so I started inspecting the view hierarchy:

UIWindow
    UIWindow
        WKWebView
            WKScrollView
            ...
        ...
UIWindow
    UIWindow
        UIView
            AVPlayerView
        UITransitionView
            UIView
                UIView
                    UIView
                        ...
                    UIView
                        ...
                    AVTouchIgnoringView
                        ...

正如预期的那样,会有额外的 UIWindow 已添加哪些可能有一个事件,而且它确实有它!

As expected there will be an additional UIWindow added which might have an event and hell yes it does have!

我扩展 viewDidAppear:添加一个新的观察者:

I extended viewDidAppear: by adding a new observer:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "windowDidBecomeVisible:", name: UIWindowDidBecomeVisibleNotification, object: nil)

并添加了相应的方法:

func windowDidBecomeVisible(notification: NSNotification) {
    for mainWindow in UIApplication.sharedApplication().windows {
        for mainWindowSubview in mainWindow.subviews {
            // this will print:
            // 1: `WKWebView` + `[WKScrollView]`
            // 2: `UIView` + `[]`
            print("\(mainWindowSubview) \(mainWindowSubview.subviews)")
}

正如我们预期的那样,它会返回我们之前检查过的视图层次结构。但遗憾的是,似乎稍后将创建 AVPlayerView

As expected it returns the view hierarchy as we inspected earlier. But unfortunately it seems like the AVPlayerView will be created later.

如果您信任您的应用程序只有 UIWindow 它将打开媒体播放器,此时你已经完成了。但是这个解决方案不会让我晚上睡觉,所以让我们更深入...

If you trust your application that the only UIWindow it'll open is the media player, you're finished at this point. But this solution wouldn't let me sleep at night, so let's go deeper...

我们需要收到有关 AVPlayerView 被添加到此无名 UIView 的通知。很明显, AVPlayerView 必须是 UIView 的子类,但由于Apple没有正式记录,我检查了< a href =https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/AVKit.framework/AVPlayerView.h =noreferrer> iOS运行时标题 AVPlayerView ,它绝对 UIView

We need to get notified about the AVPlayerView being added to this nameless UIView. It seems pretty obvious that AVPlayerView must be a subclass of UIView but since it's not officially documented by Apple I checked the iOS Runtime Headers for AVPlayerView and it definitely is a UIView.

现在我们知道 AVPlayerView UIView 的子类,它可能会添加到无名 c $ c> UIView 通过调用 addSubview:。因此,我们必须收到有关添加的视图的通知。不幸的是, UIView 没有提供观察此事件的事件。但它 调用一个名为 didAddSubview:的方法,这可能非常方便。

Now that we know that AVPlayerView is a subclass of UIView it will probably added to the nameless UIView by calling addSubview:. So we'd have to get notified about a view that was added. Unfortunately UIView doesn't provide an event for this to be observed. But it does call a method called didAddSubview: which could be very handy.

所以,让我们检查一下 AVPlayerView 将被添加到我们的应用程序的某个地方并发送通知:

So let's check wether a AVPlayerView will be added somewhere in our application and send a notification:

let originalDidAddSubviewMethod = class_getInstanceMethod(UIView.self, "didAddSubview:")
let originalDidAddSubviewImplementation = method_getImplementation(originalDidAddSubviewMethod)

typealias DidAddSubviewCFunction = @convention(c) (AnyObject, Selector, UIView) -> Void
let castedOriginalDidAddSubviewImplementation = unsafeBitCast(originalDidAddSubviewImplementation, DidAddSubviewCFunction.self)

let newDidAddSubviewImplementationBlock: @convention(block) (AnyObject!, UIView) -> Void = { (view: AnyObject!, subview: UIView) -> Void in
    castedOriginalDidAddSubviewImplementation(view, "didAddsubview:", subview)

    if object_getClass(view).description() == "AVPlayerView" {
        NSNotificationCenter.defaultCenter().postNotificationName("PlayerWillOpen", object: nil)
    }
}

let newDidAddSubviewImplementation = imp_implementationWithBlock(unsafeBitCast(newDidAddSubviewImplementationBlock, AnyObject.self))
method_setImplementation(originalDidAddSubviewMethod, newDidAddSubviewImplementation)

现在我们可以观察通知并收到相应的事件:

Now we can observe the notification and receive the corresponding event:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerWillOpen:", name: "PlayerWillOpen", object: nil)

func playerWillOpen(notification: NSNotification) {
    print("A Player will be opened now")
}



更好的通知注入



由于 AVPlayer视图不会被删除但只是取消分配我们将不得不重写我们的代码并向 AVPlayerViewController 注入一些通知。这样我们就可以获得尽可能多的通知,例如: PlayerWillAppear PlayerWillDisappear

Better notification injection

Since the AVPlayerView won't get removed but only deallocated we'll have to rewrite our code a little bit and inject some notifications to the AVPlayerViewController. That way we'll have as many notifications as we want, e.g.: PlayerWillAppear and PlayerWillDisappear:

let originalViewWillAppearMethod = class_getInstanceMethod(UIViewController.self, "viewWillAppear:")
let originalViewWillAppearImplementation = method_getImplementation(originalViewWillAppearMethod)

typealias ViewWillAppearCFunction = @convention(c) (UIViewController, Selector, Bool) -> Void
let castedOriginalViewWillAppearImplementation = unsafeBitCast(originalViewWillAppearImplementation, ViewWillAppearCFunction.self)

let newViewWillAppearImplementationBlock: @convention(block) (UIViewController!, Bool) -> Void = { (viewController: UIViewController!, animated: Bool) -> Void in
    castedOriginalViewWillAppearImplementation(viewController, "viewWillAppear:", animated)

    if viewController is AVPlayerViewController {
        NSNotificationCenter.defaultCenter().postNotificationName("PlayerWillAppear", object: nil)
    }
}

let newViewWillAppearImplementation = imp_implementationWithBlock(unsafeBitCast(newViewWillAppearImplementationBlock, AnyObject.self))
method_setImplementation(originalViewWillAppearMethod, newViewWillAppearImplementation)

let originalViewWillDisappearMethod = class_getInstanceMethod(UIViewController.self, "viewWillDisappear:")
let originalViewWillDisappearImplementation = method_getImplementation(originalViewWillDisappearMethod)

typealias ViewWillDisappearCFunction = @convention(c) (UIViewController, Selector, Bool) -> Void
let castedOriginalViewWillDisappearImplementation = unsafeBitCast(originalViewWillDisappearImplementation, ViewWillDisappearCFunction.self)

let newViewWillDisappearImplementationBlock: @convention(block) (UIViewController!, Bool) -> Void = { (viewController: UIViewController!, animated: Bool) -> Void in
    castedOriginalViewWillDisappearImplementation(viewController, "viewWillDisappear:", animated)

    if viewController is AVPlayerViewController {
        NSNotificationCenter.defaultCenter().postNotificationName("PlayerWillDisappear", object: nil)
    }
}

let newViewWillDisappearImplementation = imp_implementationWithBlock(unsafeBitCast(newViewWillDisappearImplementationBlock, AnyObject.self))
method_setImplementation(originalViewWillDisappearMethod, newViewWillDisappearImplementation)

现在我们可以观察这两个通知并且很好用:

Now we can observe these two notifications and are good to go:

 NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerWillAppear:", name: "PlayerWillAppear", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerWillDisappear:", name: "PlayerWillDisappear", object: nil)

func playerWillAppear(notification: NSNotification) {
    print("A Player will be opened now")
}

func playerWillDisappear(notification: NSNotification) {
    print("A Player will be closed now")
}



视频的网址



我花了几个小时挖掘一些iOS Runtime Headers来猜测我在哪里可以找到指向视频的URL,但我无法找到它。当我深入研究WebKit本身的一些源文件时,我不得不放弃并接受没有简单方法来做它,虽然我相信它隐藏在某个地方并且可以到达,但很可能只是付出了很多努力。

URL of the video

I spent a couple of hours digging some iOS Runtime Headers to guess where I could find the URL pointing to the video, but I couldn't manage to find it. When I was digging into some source files of WebKit itself, I had to give up and accept that there's no easy way to do it, although I believe it's somewhere hidden and can be reached, but most likely only with a lot of effort.

这篇关于如何检测正在WKWebView中播放的电影?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆