diff --git a/Action/Command.cs b/Action/Command.cs
index 302993e0..d073bdbb 100644
--- a/Action/Command.cs
+++ b/Action/Command.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Windows;
namespace XiboClient.Logic
{
@@ -40,6 +41,15 @@ public bool Run()
return true;
}
}
+ else if (CommandString == "SoftRestart")
+ {
+ // Call close.
+ Application.Current.Dispatcher.Invoke(new System.Action(() => {
+ Application.Current.MainWindow.Close();
+ }));
+
+ return true;
+ }
else
{
// Process with CMD
diff --git a/Helpers/ProxyRequestHandler.cs b/Helpers/ProxyRequestHandler.cs
new file mode 100644
index 00000000..079da60d
--- /dev/null
+++ b/Helpers/ProxyRequestHandler.cs
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2020 Xibo Signage Ltd
+ *
+ * Xibo - Digital Signage - http://www.xibo.org.uk
+ *
+ * This file is part of Xibo.
+ *
+ * Xibo is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * Xibo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Xibo. If not, see .
+ */
+using CefSharp;
+using CefSharp.Handler;
+
+namespace XiboClient.Helpers
+{
+ class ProxyRequestHandler : RequestHandler
+ {
+ protected override bool GetAuthCredentials(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
+ {
+ if (isProxy)
+ {
+ callback.Continue(ApplicationSettings.Default.ProxyUser, ApplicationSettings.Default.ProxyPassword);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/Logic/ApplicationSettings.cs b/Logic/ApplicationSettings.cs
index 1261e5bd..a9694c7b 100644
--- a/Logic/ApplicationSettings.cs
+++ b/Logic/ApplicationSettings.cs
@@ -50,7 +50,7 @@ private static readonly Lazy
///
private List ExcludedProperties;
- public string ClientVersion { get; } = "2 R256.5";
+ public string ClientVersion { get; } = "2 R256.7";
public string Version { get; } = "5";
public int ClientCodeVersion { get; } = 256;
@@ -456,6 +456,8 @@ public string ServerUri
public string ClientInformationKeyCode { get; set; }
public string XmrNetworkAddress { get; set; }
public string AggregationLevel { get; set; }
+ public string AuthServerWhitelist { get; set; }
+ public string EdgeBrowserWhitelist { get; set; }
// Download window
public string DisplayTimeZone { get; set; }
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index f9adeb81..75a2f456 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -327,10 +327,12 @@ private void MainWindow_Loaded(object sender, RoutedEventArgs e)
CefSharp.CefSharpSettings.SubprocessExitIfParentProcessClosed = true;
// Settings for Init
- CefSharp.Wpf.CefSettings settings = new CefSharp.Wpf.CefSettings();
- settings.CachePath = ApplicationSettings.Default.LibraryPath + @"\CEF";
- settings.LogFile = ApplicationSettings.Default.LibraryPath + @"\CEF\cef.log";
- settings.LogSeverity = CefSharp.LogSeverity.Fatal;
+ CefSharp.Wpf.CefSettings settings = new CefSharp.Wpf.CefSettings
+ {
+ CachePath = ApplicationSettings.Default.LibraryPath + @"\CEF",
+ LogFile = ApplicationSettings.Default.LibraryPath + @"\CEF\cef.log",
+ LogSeverity = CefSharp.LogSeverity.Fatal
+ };
settings.CefCommandLineArgs["autoplay-policy"] = "no-user-gesture-required";
CefSharp.Cef.Initialize(settings);
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 0d79e12c..9ec8b7b2 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -49,6 +49,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.256.0.5")]
-[assembly: AssemblyFileVersion("2.256.0.5")]
+[assembly: AssemblyVersion("2.256.0.7")]
+[assembly: AssemblyFileVersion("2.256.0.7")]
[assembly: Guid("3bd467a4-4ef9-466a-b156-a79c13a863f7")]
diff --git a/Rendering/Region.xaml.cs b/Rendering/Region.xaml.cs
index fd011e0f..c3f83370 100644
--- a/Rendering/Region.xaml.cs
+++ b/Rendering/Region.xaml.cs
@@ -768,7 +768,7 @@ private void StopMedia(Media media)
///
private void StopMedia(Media media, bool regionStopped)
{
- Trace.WriteLine(new LogMessage("Region", "StopMedia: Stopping..."), LogType.Audit.ToString());
+ Trace.WriteLine(new LogMessage("Region", "StopMedia: " + media.Id + " stopping, region stopped " + regionStopped), LogType.Audit.ToString());
// Dispose of the current media
try
@@ -798,6 +798,8 @@ private void StopMedia(Media media, bool regionStopped)
///
private void Media_MediaStoppedEvent(Media media)
{
+ Trace.WriteLine(new LogMessage("Region", "Media_MediaStoppedEvent: " + media.Id), LogType.Audit.ToString());
+
media.MediaStoppedEvent -= Media_MediaStoppedEvent;
media.Stopped();
diff --git a/Rendering/Video.cs b/Rendering/Video.cs
index 53be22c2..afda9453 100644
--- a/Rendering/Video.cs
+++ b/Rendering/Video.cs
@@ -23,6 +23,7 @@
using System.IO;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Threading;
namespace XiboClient.Rendering
{
@@ -34,8 +35,21 @@ class Video : Media
private bool _detectEnd = false;
private bool isLooping = false;
private readonly bool isFullScreenRequest = false;
+ private bool _openCalled = false;
+
+ ///
+ /// Should this be visible? Audio sets this to false.
+ ///
protected bool ShouldBeVisible { get; set; }
+
+ ///
+ /// Muted?
+ ///
protected bool Muted { get; set; }
+
+ ///
+ /// Stretched?
+ ///
protected bool Stretch { get; set; }
///
@@ -54,6 +68,7 @@ class Video : Media
///
public Video(RegionOptions options) : base(options)
{
+ // Videos should be visible
this.ShouldBeVisible = true;
_filePath = Uri.UnescapeDataString(options.uri).Replace('+', ' ');
@@ -79,20 +94,35 @@ public Video(RegionOptions options) : base(options)
Stretch = options.Dictionary.Get("scaleType", "aspect").ToLowerInvariant() == "stretch";
}
+ ///
+ /// Media Failed
+ ///
+ ///
+ ///
private void MediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
// Log and expire
- Trace.WriteLine(new LogMessage("Video", "MediaElement_MediaFailed: Media Failed. E = " + e.ErrorException.Message), LogType.Error.ToString());
+ Trace.WriteLine(new LogMessage("Video", "MediaElement_MediaFailed: " + this.Id + " Media Failed. E = " + e.ErrorException.Message), LogType.Error.ToString());
+
+ // Failed is the opposite of open, but we mark this as open called so that our watchman doesn't also try to expire
+ this._openCalled = true;
// Add this to a temporary blacklist so that we don't repeat it too quickly
CacheManager.Instance.AddUnsafeItem(UnsafeItemType.Media, LayoutId, Id, "Video Failed: " + e.ErrorException.Message, 120);
// Expire
- Expired = true;
+ SignalElapsedEvent();
}
- private void MediaElement_MediaEnded(object sender, System.Windows.RoutedEventArgs e)
+ ///
+ /// Media Ended
+ ///
+ ///
+ ///
+ private void MediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
+ Trace.WriteLine(new LogMessage("Video", "MediaElement_MediaEnded: " + this.Id + " Ended, looping: " + isLooping), LogType.Audit.ToString());
+
// Should we loop?
if (isLooping)
{
@@ -106,12 +136,14 @@ private void MediaElement_MediaEnded(object sender, System.Windows.RoutedEventAr
}
///
- /// Media is loaded
+ /// MediaElement has been added to the visual tree
///
///
///
private void MediaElement_Loaded(object sender, RoutedEventArgs e)
{
+ Trace.WriteLine(new LogMessage("Video", "MediaElement_Loaded: " + this.Id + " Control loaded, calling Play."), LogType.Audit.ToString());
+
try
{
this.mediaElement.Play();
@@ -119,10 +151,36 @@ private void MediaElement_Loaded(object sender, RoutedEventArgs e)
catch (Exception ex)
{
// Problem calling play, we should expire.
- Trace.WriteLine(new LogMessage("Video", "MediaElement_Loaded: Media Failed. E = " + ex.Message), LogType.Error.ToString());
+ Trace.WriteLine(new LogMessage("Video", "MediaElement_Loaded: " + this.Id + " Media Failed. E = " + ex.Message), LogType.Error.ToString());
}
+
+ // We make a watchman to check that the video actually gets loaded.
+ var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(4) };
+ timer.Tick += (timerSender, args) =>
+ {
+ // You only tick once
+ timer.Stop();
+
+ // Check to see if open has been called.
+ if (!_openCalled)
+ {
+ Trace.WriteLine(new LogMessage("Video", "MediaElement_Loaded: " + this.Id + " Open not called after 4 seconds, marking unsafe and Expiring."), LogType.Error.ToString());
+
+ // Add this to a temporary blacklist so that we don't repeat it too quickly
+ CacheManager.Instance.AddUnsafeItem(UnsafeItemType.Media, LayoutId, Id, "Video Failed: Open not called after 4 seconds", 120);
+
+ // Expire
+ SignalElapsedEvent();
+ }
+ };
+
+ timer.Start();
}
+ ///
+ /// Render
+ ///
+ ///
public override void RenderMedia(double position)
{
// Save the position
@@ -134,7 +192,7 @@ public override void RenderMedia(double position)
if (uri.IsFile && !File.Exists(_filePath))
{
- Trace.WriteLine(new LogMessage("Video", "RenderMedia: File " + _filePath + " not found."));
+ Trace.WriteLine(new LogMessage("Video", "RenderMedia: " + this.Id + ", File " + _filePath + " not found."));
throw new FileNotFoundException();
}
@@ -143,13 +201,22 @@ public override void RenderMedia(double position)
this.mediaElement.Volume = this.volume;
this.mediaElement.IsMuted = this.Muted;
this.mediaElement.LoadedBehavior = MediaState.Manual;
+ this.mediaElement.UnloadedBehavior = MediaState.Close;
+ // This is false if we're an audio module, otherwise video.
if (!this.ShouldBeVisible)
{
this.mediaElement.Width = 0;
this.mediaElement.Height = 0;
this.mediaElement.Visibility = Visibility.Hidden;
}
+ else
+ {
+ // Assert the Width/Height of the Parent
+ this.mediaElement.Width = Width;
+ this.mediaElement.Height = Height;
+ this.mediaElement.Visibility = Visibility.Visible;
+ }
// Handle stretching
if (Stretch)
@@ -158,9 +225,16 @@ public override void RenderMedia(double position)
}
// Events
+ // MediaOpened is called after we've called Play()
this.mediaElement.MediaOpened += MediaElement_MediaOpened;
+
+ // Loaded is from the Framework and is called when the MediaElement is added to the visual tree (we call play in here)
this.mediaElement.Loaded += MediaElement_Loaded;
+
+ // Media ended is called when the media file has finished playing
this.mediaElement.MediaEnded += MediaElement_MediaEnded;
+
+ // Media Failed is called if the media file cannot be opened
this.mediaElement.MediaFailed += MediaElement_MediaFailed;
// Do we need to determine the end time ourselves?
@@ -183,7 +257,7 @@ public override void RenderMedia(double position)
this.MediaScene.Children.Add(this.mediaElement);
- Trace.WriteLine(new LogMessage("Video", "RenderMedia: Video Started"), LogType.Audit.ToString());
+ Trace.WriteLine(new LogMessage("Video", "RenderMedia: " + this.Id + ", added MediaElement and set source, detect end is " + _detectEnd), LogType.Audit.ToString());
}
catch (Exception ex)
{
@@ -201,12 +275,18 @@ public override void RenderMedia(double position)
///
private void MediaElement_MediaOpened(object sender, RoutedEventArgs e)
{
- Debug.WriteLine("MediaElement_MediaOpened", "Video");
+ Trace.WriteLine(new LogMessage("Video", "MediaElement_MediaOpened: " + this.Id + " Opened, seek to: " + this._position), LogType.Audit.ToString());
+
+ // Open has been called.
+ this._openCalled = true;
// Try to seek
if (this._position > 0)
{
this.mediaElement.Position = TimeSpan.FromSeconds(this._position);
+
+ // Set the position to 0, so that if we loop around again we start from the beginning
+ this._position = 0;
}
}
@@ -215,6 +295,8 @@ private void MediaElement_MediaOpened(object sender, RoutedEventArgs e)
///
public override void Stopped()
{
+ Trace.WriteLine(new LogMessage("Video", "Stopped: " + this.Id), LogType.Audit.ToString());
+
// Remove the event handlers
this.mediaElement.MediaOpened -= MediaElement_MediaOpened;
this.mediaElement.Loaded -= MediaElement_Loaded;
diff --git a/Rendering/WebCef.cs b/Rendering/WebCef.cs
index 9f4cca4b..84bf4bbb 100644
--- a/Rendering/WebCef.cs
+++ b/Rendering/WebCef.cs
@@ -51,6 +51,34 @@ public override void RenderMedia(double position)
Name = "region_" + this.regionId
};
+ // Configure run time CEF settings?
+ if (!string.IsNullOrEmpty(ApplicationSettings.Default.AuthServerWhitelist)
+ || !string.IsNullOrEmpty(ApplicationSettings.Default.ProxyUser))
+ {
+ CefSharp.Cef.UIThreadTaskFactory.StartNew(() =>
+ {
+ // NTLM/Auth Server White Lists.
+ if (!string.IsNullOrEmpty(ApplicationSettings.Default.AuthServerWhitelist))
+ {
+ if (!webView.RequestContext.SetPreference("auth.server_whitelist", ApplicationSettings.Default.AuthServerWhitelist, out string error))
+ {
+ Trace.WriteLine(new LogMessage("WebCef", "RenderMedia: auth.server_whitelist. e = " + error), LogType.Error.ToString());
+ }
+
+ if (!webView.RequestContext.SetPreference("auth.negotiate_delegate_whitelist", ApplicationSettings.Default.AuthServerWhitelist, out string error2))
+ {
+ Trace.WriteLine(new LogMessage("WebCef", "RenderMedia: auth.negotiate_delegate_whitelist. e = " + error2), LogType.Error.ToString());
+ }
+ }
+
+ // Proxy
+ if (!string.IsNullOrEmpty(ApplicationSettings.Default.ProxyUser))
+ {
+ webView.RequestHandler = new ProxyRequestHandler();
+ }
+ });
+ }
+
webView.Visibility = System.Windows.Visibility.Hidden;
webView.Loaded += WebView_Loaded;
webView.LoadError += WebView_LoadError;
@@ -106,7 +134,7 @@ private void WebView_Loaded(object sender, System.Windows.RoutedEventArgs e)
private void WebView_LoadError(object sender, CefSharp.LoadErrorEventArgs e)
{
- Trace.WriteLine(new LogMessage("EdgeWebMedia", "Cannot navigate. e = " + e.ToString()), LogType.Error.ToString());
+ Trace.WriteLine(new LogMessage("WebCef", "WebView_LoadError: Cannot navigate. e = " + e.ToString()), LogType.Error.ToString());
// This should exipre the media
Duration = 5;
diff --git a/Rendering/WebMedia.cs b/Rendering/WebMedia.cs
index 5ffecbda..d9d9c4d0 100644
--- a/Rendering/WebMedia.cs
+++ b/Rendering/WebMedia.cs
@@ -472,16 +472,22 @@ public static string ReadBrowserType(string template)
///
public static WebMedia GetConfiguredWebMedia(RegionOptions options)
{
- WebMedia media;
if (ApplicationSettings.Default.FallbackToInternetExplorer)
{
- media = new WebIe(options);
+ return new WebIe(options);
}
- else
+ else if (!string.IsNullOrEmpty(ApplicationSettings.Default.EdgeBrowserWhitelist))
{
- media = new WebCef(options);
+ // Decode the URL
+ string url = Uri.UnescapeDataString(options.uri);
+
+ if (url.Contains(ApplicationSettings.Default.EdgeBrowserWhitelist))
+ {
+ return new WebEdge(options);
+ }
}
- return media;
+
+ return new WebCef(options);
}
///
diff --git a/XiboClient.csproj b/XiboClient.csproj
index c6682343..11587180 100644
--- a/XiboClient.csproj
+++ b/XiboClient.csproj
@@ -118,6 +118,7 @@
+
InfoScreen.xaml