[Bug][iOS] Restricted YouTube video embedded in local HTML in WebView won't play, stating "Video is unavailable"
#11,504 建立於 2020年7月22日
描述
Description
This may be considered a feature request rather than a bug, so change as necessary.
A YouTube video that has restrictions set won't play in a Xamarin.Forms WebView, with YouTube message saying "Video is unavailable." This occurs when the video is embedded in local HTML string, and no base url or an improper base url is provided to the HtmlWebViewSource. e.g.:
var browser = new WebView();
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = @"<html>
<body>
<div style=' position: relative; padding-bottom: 56.25%; padding-top: 25px; overflow: hidden;'>
<iframe style='position: absolute; top: 0; left: 0; width: 100%; height: 100%;' src='https://www.youtube.com/embed/ssP0GrK8Uws' frameborder='0' allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>
</div>
</body>
</html>";
browser.Source = htmlSource;
On android, the solution is simply to set the BaseUrl property on the HtmlWebViewSource to "https://www.youtube.com", e.g.
htmlSource.BaseUrl = "http://www.youtube.com";
However on iOS this does not work. I believe this is due to this line in the Xam.Forms source code: https://github.com/xamarin/Xamarin.Forms/blob/main/Xamarin.Forms.Platform.iOS/Renderers/WkWebViewRenderer.cs#L99
LoadHtmlString(html, baseUrl == null ? new NSUrl(NSBundle.MainBundle.BundlePath, true) : new NSUrl(baseUrl, true));
The issue is the second parameter for new NSUrl(baseUrl, true));, which according to apple docs using that constructor assumes that this is a file url. See: https://developer.apple.com/documentation/foundation/nsurl/1417505-initfileurlwithpath?language=objc
However, setting that second parameter to false does not allow the YouTube restricted video to play either (I believe because a file base url is expected, but we are setting a web base url), you have to use the single parameter constructor for NSUrl, e.g.:
LoadHtmlString(html, baseUrl == null ? new NSUrl(NSBundle.MainBundle.BundlePath, true) : new NSUrl(baseUrl)); // using single parameter constructor for NSUrl here
This can be worked around using a custom renderer for iOS:
class CustomWebViewRenderer : Xamarin.Forms.Platform.iOS.WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var webView = e.NewElement as WebView;
var source = webView.Source as HtmlWebViewSource;
LoadHtmlString(source.Html,
source.BaseUrl == null ? new NSUrl(NSBundle.MainBundle.BundlePath, true) : (source.BaseUrl.StartsWith("http") ? new NSUrl(source.BaseUrl) : new NSUrl(source.BaseUrl, true)));
}
}
}
The above preserves the existing behavior unless the BaseUrl provided by the end developer starts with "http".
Steps to Reproduce
- Open the attached test project (derived from the Working with WebView sample)
- Launch to an iOS device or simulator
- Select the Local tab
- Click on the YouTube video's play button
Expected Behavior
Video will play
Actual Behavior
Video does not play. with a "Video Unavailable" message.
Add'l notes
If you select the BaseUrl tab, you will see that the same video will play as the web view in this tab uses the above custom renderer.
Worth noting is that it does not seem to matter what the web BaseUrl is, IOW it does not have to be "https://www.youtube.com", it can be anything. The important part seems to be to not use that NSUrl constructor that takes a boolean for IsDirectory
Basic Information
- Version with issue: 4.7
- Last known good version: Unknown
- IDE: Visual Studio 2019
- Platform Target Frameworks:
- iOS: 13.5
- Nuget Packages: Forms and Essentials
Reproduction Link
Workaround
See above for custom renderer that can workaround this issue.