diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index ee6870271..18cef5819 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -57,3 +57,4 @@
- [Detector1](https://github.com/Detector1)
- [BlackIce013](https://github.com/blackice013)
- [mporcas] (https://github.com/mporcas)
+ - [tikuf] (https://github.com/tikuf/)
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 52707c3c6..8754e57a1 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -62,7 +62,7 @@ namespace MediaBrowser.Api
{
var jobCount = _activeTranscodingJobs.Count;
- Parallel.ForEach(_activeTranscodingJobs, OnTranscodeKillTimerStopped);
+ Parallel.ForEach(_activeTranscodingJobs, KillTranscodingJob);
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if (jobCount > 0)
@@ -84,7 +84,8 @@ namespace MediaBrowser.Api
/// The process.
/// if set to true [is video].
/// The start time ticks.
- public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, bool isVideo, long? startTimeTicks)
+ /// The source path.
+ public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, bool isVideo, long? startTimeTicks, string sourcePath)
{
lock (_activeTranscodingJobs)
{
@@ -95,7 +96,8 @@ namespace MediaBrowser.Api
Process = process,
ActiveRequestCount = 1,
IsVideo = isVideo,
- StartTimeTicks = startTimeTicks
+ StartTimeTicks = startTimeTicks,
+ SourcePath = sourcePath
});
}
}
@@ -178,7 +180,7 @@ namespace MediaBrowser.Api
if (job.ActiveRequestCount == 0)
{
- var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 60000;
+ var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 180000;
if (job.KillTimer == null)
{
@@ -196,10 +198,47 @@ namespace MediaBrowser.Api
/// Called when [transcode kill timer stopped].
///
/// The state.
- private async void OnTranscodeKillTimerStopped(object state)
+ private void OnTranscodeKillTimerStopped(object state)
{
var job = (TranscodingJob)state;
+ KillTranscodingJob(job);
+ }
+
+ ///
+ /// Kills the single transcoding job.
+ ///
+ /// The source path.
+ internal void KillSingleTranscodingJob(string sourcePath)
+ {
+ if (string.IsNullOrEmpty(sourcePath))
+ {
+ throw new ArgumentNullException("sourcePath");
+ }
+
+ var jobs = new List();
+
+ lock (_activeTranscodingJobs)
+ {
+ // This is really only needed for HLS.
+ // Progressive streams can stop on their own reliably
+ jobs.AddRange(_activeTranscodingJobs.Where(i => string.Equals(sourcePath, i.SourcePath) && i.Type == TranscodingJobType.Hls));
+ }
+
+ // This method of killing is a bit of a shortcut, but it saves clients from having to send a request just for that
+ // But we can only kill if there's one active job. If there are more we won't know which one to stop
+ if (jobs.Count == 1)
+ {
+ KillTranscodingJob(jobs.First());
+ }
+ }
+
+ ///
+ /// Kills the transcoding job.
+ ///
+ /// The job.
+ private async void KillTranscodingJob(TranscodingJob job)
+ {
lock (_activeTranscodingJobs)
{
_activeTranscodingJobs.Remove(job);
@@ -373,6 +412,7 @@ namespace MediaBrowser.Api
public bool IsVideo { get; set; }
public long? StartTimeTicks { get; set; }
+ public string SourcePath { get; set; }
}
///
diff --git a/MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs b/MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs
new file mode 100644
index 000000000..d225bdd99
--- /dev/null
+++ b/MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs
@@ -0,0 +1,181 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Logging;
+using ServiceStack.Common.Web;
+using ServiceStack.ServiceHost;
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Api
+{
+ public class AuthorizationRequestFilterAttribute : Attribute, IHasRequestFilter
+ {
+ //This property will be resolved by the IoC container
+ ///
+ /// Gets or sets the user manager.
+ ///
+ /// The user manager.
+ public IUserManager UserManager { get; set; }
+
+ public ISessionManager SessionManager { get; set; }
+
+ ///
+ /// Gets or sets the logger.
+ ///
+ /// The logger.
+ public ILogger Logger { get; set; }
+
+ ///
+ /// The request filter is executed before the service.
+ ///
+ /// The http request wrapper
+ /// The http response wrapper
+ /// The request DTO
+ public void RequestFilter(IHttpRequest request, IHttpResponse response, object requestDto)
+ {
+ //This code is executed before the service
+
+ var auth = GetAuthorization(request);
+
+ if (auth != null)
+ {
+ User user = null;
+
+ if (auth.ContainsKey("UserId"))
+ {
+ var userId = auth["UserId"];
+
+ if (!string.IsNullOrEmpty(userId))
+ {
+ user = UserManager.GetUserById(new Guid(userId));
+ }
+ }
+
+ string deviceId;
+ string device;
+ string client;
+ string version;
+
+ auth.TryGetValue("DeviceId", out deviceId);
+ auth.TryGetValue("Device", out device);
+ auth.TryGetValue("Client", out client);
+ auth.TryGetValue("Version", out version);
+
+ if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
+ {
+ SessionManager.LogConnectionActivity(client, version, deviceId, device, user);
+ }
+ }
+ }
+
+ ///
+ /// Gets the auth.
+ ///
+ /// The HTTP req.
+ /// Dictionary{System.StringSystem.String}.
+ public static Dictionary GetAuthorization(IHttpRequest httpReq)
+ {
+ var auth = httpReq.Headers[HttpHeaders.Authorization];
+
+ return GetAuthorization(auth);
+ }
+
+ ///
+ /// Gets the authorization.
+ ///
+ /// The HTTP req.
+ /// Dictionary{System.StringSystem.String}.
+ public static AuthorizationInfo GetAuthorization(IRequestContext httpReq)
+ {
+ var header = httpReq.GetHeader("Authorization");
+
+ var auth = GetAuthorization(header);
+
+ string userId;
+ string deviceId;
+ string device;
+ string client;
+ string version;
+
+ auth.TryGetValue("UserId", out userId);
+ auth.TryGetValue("DeviceId", out deviceId);
+ auth.TryGetValue("Device", out device);
+ auth.TryGetValue("Client", out client);
+ auth.TryGetValue("Version", out version);
+
+ return new AuthorizationInfo
+ {
+ Client = client,
+ Device = device,
+ DeviceId = deviceId,
+ UserId = userId,
+ Version = version
+ };
+ }
+
+ ///
+ /// Gets the authorization.
+ ///
+ /// The authorization header.
+ /// Dictionary{System.StringSystem.String}.
+ private static Dictionary GetAuthorization(string authorizationHeader)
+ {
+ if (authorizationHeader == null) return null;
+
+ var parts = authorizationHeader.Split(' ');
+
+ // There should be at least to parts
+ if (parts.Length < 2) return null;
+
+ // It has to be a digest request
+ if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ // Remove uptil the first space
+ authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
+ parts = authorizationHeader.Split(',');
+
+ var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var item in parts)
+ {
+ var param = item.Trim().Split(new[] { '=' }, 2);
+ result.Add(param[0], param[1].Trim(new[] { '"' }));
+ }
+
+ return result;
+ }
+
+ ///
+ /// A new shallow copy of this filter is used on every request.
+ ///
+ /// IHasRequestFilter.
+ public IHasRequestFilter Copy()
+ {
+ return this;
+ }
+
+ ///
+ /// Order in which Request Filters are executed.
+ /// <0 Executed before global request filters
+ /// >0 Executed after global request filters
+ ///
+ /// The priority.
+ public int Priority
+ {
+ get { return 0; }
+ }
+ }
+
+ public class AuthorizationInfo
+ {
+ public string UserId;
+ public string DeviceId;
+ public string Device;
+ public string Client;
+ public string Version;
+ }
+}
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index b3f5027e0..069bc0fe1 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -2,9 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
-using ServiceStack.Common.Web;
using ServiceStack.ServiceHost;
using System;
using System.Collections.Generic;
@@ -15,7 +13,7 @@ namespace MediaBrowser.Api
///
/// Class BaseApiService
///
- [RequestFilter]
+ [AuthorizationRequestFilter]
public class BaseApiService : IHasResultFactory, IRestfulService
{
///
@@ -308,147 +306,4 @@ namespace MediaBrowser.Api
return item;
}
}
-
- ///
- /// Class RequestFilterAttribute
- ///
- public class RequestFilterAttribute : Attribute, IHasRequestFilter
- {
- //This property will be resolved by the IoC container
- ///
- /// Gets or sets the user manager.
- ///
- /// The user manager.
- public IUserManager UserManager { get; set; }
-
- public ISessionManager SessionManager { get; set; }
-
- ///
- /// Gets or sets the logger.
- ///
- /// The logger.
- public ILogger Logger { get; set; }
-
- ///
- /// The request filter is executed before the service.
- ///
- /// The http request wrapper
- /// The http response wrapper
- /// The request DTO
- public void RequestFilter(IHttpRequest request, IHttpResponse response, object requestDto)
- {
- //This code is executed before the service
-
- var auth = GetAuthorization(request);
-
- if (auth != null)
- {
- User user = null;
-
- if (auth.ContainsKey("UserId"))
- {
- var userId = auth["UserId"];
-
- if (!string.IsNullOrEmpty(userId))
- {
- user = UserManager.GetUserById(new Guid(userId));
- }
- }
-
- string deviceId;
- string device;
- string client;
- string version;
-
- auth.TryGetValue("DeviceId", out deviceId);
- auth.TryGetValue("Device", out device);
- auth.TryGetValue("Client", out client);
- auth.TryGetValue("Version", out version);
-
- if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
- {
- SessionManager.LogConnectionActivity(client, version, deviceId, device, user);
- }
- }
- }
-
- ///
- /// Gets the auth.
- ///
- /// The HTTP req.
- /// Dictionary{System.StringSystem.String}.
- public static Dictionary GetAuthorization(IHttpRequest httpReq)
- {
- var auth = httpReq.Headers[HttpHeaders.Authorization];
-
- return GetAuthorization(auth);
- }
-
- ///
- /// Gets the authorization.
- ///
- /// The HTTP req.
- /// Dictionary{System.StringSystem.String}.
- public static Dictionary GetAuthorization(IRequestContext httpReq)
- {
- var auth = httpReq.GetHeader("Authorization");
-
- return GetAuthorization(auth);
- }
-
- ///
- /// Gets the authorization.
- ///
- /// The authorization header.
- /// Dictionary{System.StringSystem.String}.
- private static Dictionary GetAuthorization(string authorizationHeader)
- {
- if (authorizationHeader == null) return null;
-
- var parts = authorizationHeader.Split(' ');
-
- // There should be at least to parts
- if (parts.Length < 2) return null;
-
- // It has to be a digest request
- if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
- // Remove uptil the first space
- authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
- parts = authorizationHeader.Split(',');
-
- var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- foreach (var item in parts)
- {
- var param = item.Trim().Split(new[] { '=' }, 2);
- result.Add(param[0], param[1].Trim(new[] { '"' }));
- }
-
- return result;
- }
-
- ///
- /// A new shallow copy of this filter is used on every request.
- ///
- /// IHasRequestFilter.
- public IHasRequestFilter Copy()
- {
- return this;
- }
-
- ///
- /// Order in which Request Filters are executed.
- /// <0 Executed before global request filters
- /// >0 Executed after global request filters
- ///
- /// The priority.
- public int Priority
- {
- get { return 0; }
- }
- }
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index c7cca812f..4f54b5249 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -36,29 +38,25 @@
Always
-
- False
- ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
-
-
- False
- ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
-
-
+
+ ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
+
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
+
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
+
+
+ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
+
+
@@ -70,6 +68,7 @@
+
@@ -88,6 +87,8 @@
+
+
@@ -128,22 +129,24 @@
- {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
MediaBrowser.Common
- {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}
MediaBrowser.Controller
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
-
+
+
+
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index e31a112d5..c782c243d 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -613,7 +613,7 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true
};
- ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, video != null, state.Request.StartTimeTicks);
+ ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, video != null, state.Request.StartTimeTicks, state.Item.Path);
Logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
diff --git a/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs b/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs
index d7ee73a9e..6e36ba0ad 100644
--- a/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using ServiceStack.ServiceHost;
using System;
-using System.IO;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -20,27 +19,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
- ///
- /// Class GetHlsAudioSegment
- ///
- [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
- [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
- public class GetHlsAudioSegment
- {
- ///
- /// Gets or sets the id.
- ///
- /// The id.
- public string Id { get; set; }
-
- ///
- /// Gets or sets the segment id.
- ///
- /// The segment id.
- public string SegmentId { get; set; }
- }
-
///
/// Class AudioHlsService
///
@@ -59,20 +37,6 @@ namespace MediaBrowser.Api.Playback.Hls
{
}
- ///
- /// Gets the specified request.
- ///
- /// The request.
- /// System.Object.
- public object Get(GetHlsAudioSegment request)
- {
- var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
-
- file = Path.Combine(ApplicationPaths.EncodedMediaCachePath, file);
-
- return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
- }
-
///
/// Gets the specified request.
///
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index e680546b0..05441bba7 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -10,7 +10,6 @@ using MediaBrowser.Model.IO;
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -213,29 +212,6 @@ namespace MediaBrowser.Api.Playback.Hls
return count;
}
- protected void ExtendHlsTimer(string itemId, string playlistId)
- {
- var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
-
- foreach (var playlist in Directory.EnumerateFiles(ApplicationPaths.EncodedMediaCachePath, "*.m3u8")
- .Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
- .ToList())
- {
- ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
-
- // Avoid implicitly captured closure
- var playlist1 = playlist;
-
- Task.Run(async () =>
- {
- // This is an arbitrary time period corresponding to when the request completes.
- await Task.Delay(30000).ConfigureAwait(false);
-
- ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist1, TranscodingJobType.Hls);
- });
- }
- }
-
///
/// Gets the command line arguments.
///
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs
new file mode 100644
index 000000000..44996c99f
--- /dev/null
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs
@@ -0,0 +1,53 @@
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Logging;
+using ServiceStack.ServiceHost;
+using ServiceStack.Text.Controller;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Api.Playback.Hls
+{
+ public class HlsSegmentResponseFilter : Attribute, IHasResponseFilter
+ {
+ public ILogger Logger { get; set; }
+ public IServerApplicationPaths ApplicationPaths { get; set; }
+
+ public void ResponseFilter(IHttpRequest req, IHttpResponse res, object response)
+ {
+ var pathInfo = PathInfo.Parse(req.PathInfo);
+ var itemId = pathInfo.GetArgumentValue(1);
+ var playlistId = pathInfo.GetArgumentValue(3);
+
+ OnEndRequest(itemId, playlistId);
+ }
+
+ public IHasResponseFilter Copy()
+ {
+ return this;
+ }
+
+ public int Priority
+ {
+ get { return -1; }
+ }
+
+ ///
+ /// Called when [end request].
+ ///
+ /// The item id.
+ /// The playlist id.
+ protected void OnEndRequest(string itemId, string playlistId)
+ {
+ Logger.Info("OnEndRequest " + playlistId);
+ var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
+
+ foreach (var playlist in Directory.EnumerateFiles(ApplicationPaths.EncodedMediaCachePath, "*.m3u8")
+ .Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
+ .ToList())
+ {
+ ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
new file mode 100644
index 000000000..f1fa86f78
--- /dev/null
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -0,0 +1,147 @@
+using MediaBrowser.Controller;
+using ServiceStack.ServiceHost;
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Playback.Hls
+{
+ ///
+ /// Class GetHlsAudioSegment
+ ///
+ [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
+ [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
+ [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
+ public class GetHlsAudioSegment
+ {
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ public string Id { get; set; }
+
+ ///
+ /// Gets or sets the segment id.
+ ///
+ /// The segment id.
+ public string SegmentId { get; set; }
+ }
+
+ ///
+ /// Class GetHlsVideoSegment
+ ///
+ [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
+ [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
+ public class GetHlsVideoSegment
+ {
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ public string Id { get; set; }
+
+ public string PlaylistId { get; set; }
+
+ ///
+ /// Gets or sets the segment id.
+ ///
+ /// The segment id.
+ public string SegmentId { get; set; }
+ }
+
+ ///
+ /// Class GetHlsVideoSegment
+ ///
+ [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
+ [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
+ public class GetHlsPlaylist
+ {
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ public string Id { get; set; }
+
+ public string PlaylistId { get; set; }
+ }
+
+ public class HlsSegmentService : BaseApiService
+ {
+ private readonly IServerApplicationPaths _appPaths;
+
+ public HlsSegmentService(IServerApplicationPaths appPaths)
+ {
+ _appPaths = appPaths;
+ }
+
+ public object Get(GetHlsPlaylist request)
+ {
+ OnBeginRequest(request.PlaylistId);
+
+ var file = request.PlaylistId + Path.GetExtension(RequestContext.PathInfo);
+
+ file = Path.Combine(_appPaths.EncodedMediaCachePath, file);
+
+ return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
+ }
+
+ ///
+ /// Gets the specified request.
+ ///
+ /// The request.
+ /// System.Object.
+ public object Get(GetHlsVideoSegment request)
+ {
+ var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
+
+ file = Path.Combine(_appPaths.EncodedMediaCachePath, file);
+
+ OnBeginRequest(request.PlaylistId);
+
+ return ResultFactory.GetStaticFileResult(RequestContext, file);
+ }
+
+ ///
+ /// Gets the specified request.
+ ///
+ /// The request.
+ /// System.Object.
+ public object Get(GetHlsAudioSegment request)
+ {
+ var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
+
+ file = Path.Combine(_appPaths.EncodedMediaCachePath, file);
+
+ return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
+ }
+
+ ///
+ /// Called when [begin request].
+ ///
+ /// The playlist id.
+ protected void OnBeginRequest(string playlistId)
+ {
+ var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
+
+ foreach (var playlist in Directory.EnumerateFiles(_appPaths.EncodedMediaCachePath, "*.m3u8")
+ .Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
+ .ToList())
+ {
+ ExtendPlaylistTimer(playlist);
+ }
+ }
+
+ private void ExtendPlaylistTimer(string playlist)
+ {
+ ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
+
+ Task.Run(async () =>
+ {
+ await Task.Delay(20000).ConfigureAwait(false);
+
+ ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
+ });
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 901b27688..4694b68a1 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.IO;
using ServiceStack.ServiceHost;
using System;
-using System.IO;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -31,44 +30,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
- ///
- /// Class GetHlsVideoSegment
- ///
- [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
- public class GetHlsVideoSegment
- {
- ///
- /// Gets or sets the id.
- ///
- /// The id.
- public string Id { get; set; }
-
- public string PlaylistId { get; set; }
-
- ///
- /// Gets or sets the segment id.
- ///
- /// The segment id.
- public string SegmentId { get; set; }
- }
-
- ///
- /// Class GetHlsVideoSegment
- ///
- [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
- public class GetHlsPlaylist
- {
- ///
- /// Gets or sets the id.
- ///
- /// The id.
- public string Id { get; set; }
-
- public string PlaylistId { get; set; }
- }
-
///
/// Class VideoHlsService
///
@@ -82,38 +43,12 @@ namespace MediaBrowser.Api.Playback.Hls
/// The library manager.
/// The iso manager.
/// The media encoder.
+ /// The dto service.
public VideoHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService)
: base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService)
{
}
- ///
- /// Gets the specified request.
- ///
- /// The request.
- /// System.Object.
- public object Get(GetHlsVideoSegment request)
- {
- ExtendHlsTimer(request.Id, request.PlaylistId);
-
- var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
-
- file = Path.Combine(ApplicationPaths.EncodedMediaCachePath, file);
-
- return ResultFactory.GetStaticFileResult(RequestContext, file);
- }
-
- public object Get(GetHlsPlaylist request)
- {
- ExtendHlsTimer(request.Id, request.PlaylistId);
-
- var file = request.PlaylistId + Path.GetExtension(RequestContext.PathInfo);
-
- file = Path.Combine(ApplicationPaths.EncodedMediaCachePath, file);
-
- return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
- }
-
///
/// Gets the specified request.
///
diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs
index cad3c4384..5888d9fba 100644
--- a/MediaBrowser.Api/SessionsService.cs
+++ b/MediaBrowser.Api/SessionsService.cs
@@ -1,7 +1,5 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using ServiceStack.ServiceHost;
using System;
@@ -189,6 +187,7 @@ namespace MediaBrowser.Api
/// Initializes a new instance of the class.
///
/// The session manager.
+ /// The dto service.
public SessionsService(ISessionManager sessionManager, IDtoService dtoService)
{
_sessionManager = sessionManager;
@@ -214,277 +213,82 @@ namespace MediaBrowser.Api
public void Post(SendPlaystateCommand request)
{
- var task = SendPlaystateCommand(request);
+ var command = new PlaystateRequest
+ {
+ Command = request.Command,
+ SeekPositionTicks = request.SeekPositionTicks
+ };
+
+ var task = _sessionManager.SendPlaystateCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
- private async Task SendPlaystateCommand(SendPlaystateCommand request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
-
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
-
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
-
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage
- {
- MessageType = "Playstate",
-
- Data = new PlaystateRequest
- {
- Command = request.Command,
- SeekPositionTicks = request.SeekPositionTicks
- }
-
- }, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error sending web socket message", ex);
- }
- }
- else
- {
- throw new InvalidOperationException("The requested session does not have an open web socket.");
- }
- }
-
///
/// Posts the specified request.
///
/// The request.
public void Post(BrowseTo request)
{
- var task = BrowseTo(request);
+ var command = new BrowseRequest
+ {
+ Context = request.Context,
+ ItemId = request.ItemId,
+ ItemName = request.ItemName,
+ ItemType = request.ItemType
+ };
+
+ var task = _sessionManager.SendBrowseCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
- ///
- /// Browses to.
- ///
- /// The request.
- /// Task.
- ///
- ///
- /// The requested session does not have an open web socket.
- private async Task BrowseTo(BrowseTo request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
-
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
-
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
-
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage
- {
- MessageType = "Browse",
- Data = request
-
- }, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error sending web socket message", ex);
- }
- }
- else
- {
- throw new InvalidOperationException("The requested session does not have an open web socket.");
- }
- }
-
///
/// Posts the specified request.
///
/// The request.
public void Post(SendSystemCommand request)
{
- var task = SendSystemCommand(request);
+ var task = _sessionManager.SendSystemCommand(request.Id, request.Command, CancellationToken.None);
Task.WaitAll(task);
}
- private async Task SendSystemCommand(SendSystemCommand request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
-
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
-
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
-
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage
- {
- MessageType = "SystemCommand",
- Data = request.Command.ToString()
-
- }, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error sending web socket message", ex);
- }
- }
- else
- {
- throw new InvalidOperationException("The requested session does not have an open web socket.");
- }
- }
-
///
/// Posts the specified request.
///
/// The request.
public void Post(SendMessageCommand request)
{
- var task = SendMessageCommand(request);
+ var command = new MessageCommand
+ {
+ Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
+ TimeoutMs = request.TimeoutMs,
+ Text = request.Text
+ };
+
+ var task = _sessionManager.SendMessageCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
- private async Task SendMessageCommand(SendMessageCommand request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
-
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
-
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
-
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage
- {
- MessageType = "MessageCommand",
-
- Data = new MessageCommand
- {
- Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
- TimeoutMs = request.TimeoutMs,
- Text = request.Text
- }
-
- }, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error sending web socket message", ex);
- }
- }
- else
- {
- throw new InvalidOperationException("The requested session does not have an open web socket.");
- }
- }
-
///
/// Posts the specified request.
///
/// The request.
public void Post(Play request)
{
- var task = Play(request);
+ var command = new PlayRequest
+ {
+ ItemIds = request.ItemIds.Split(',').ToArray(),
+
+ PlayCommand = request.PlayCommand,
+ StartPositionTicks = request.StartPositionTicks
+ };
+
+ var task = _sessionManager.SendPlayCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
-
- ///
- /// Plays the specified request.
- ///
- /// The request.
- /// Task.
- ///
- ///
- /// The requested session does not have an open web socket.
- private async Task Play(Play request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
-
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
-
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
-
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage
- {
- MessageType = "Play",
-
- Data = new PlayRequest
- {
- ItemIds = request.ItemIds.Split(',').ToArray(),
-
- PlayCommand = request.PlayCommand,
- StartPositionTicks = request.StartPositionTicks
- }
-
- }, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error sending web socket message", ex);
- }
- }
- else
- {
- throw new InvalidOperationException("The requested session does not have an open web socket.");
- }
- }
}
}
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index ab3e2af19..abd42910f 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -186,7 +186,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public DateTime? DatePlayed { get; set; }
-
+
///
/// Gets or sets the id.
///
@@ -224,6 +224,13 @@ namespace MediaBrowser.Api.UserLibrary
[Api(Description = "Reports that a user has begun playing an item")]
public class OnPlaybackStart : IReturnVoid
{
+ public OnPlaybackStart()
+ {
+ // Have to default these until all clients have a chance to incorporate them
+ CanSeek = true;
+ QueueableMediaTypes = "Audio,Video,Book,Game";
+ }
+
///
/// Gets or sets the user id.
///
@@ -237,6 +244,20 @@ namespace MediaBrowser.Api.UserLibrary
/// The id.
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether this is likes.
+ ///
+ /// true if likes; otherwise, false.
+ [ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
+ public bool CanSeek { get; set; }
+
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ [ApiMember(Name = "QueueableMediaTypes", Description = "A list of media types that can be queued from this item, comma delimited. Audio,Video,Book,Game", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
+ public string QueueableMediaTypes { get; set; }
}
///
@@ -378,6 +399,8 @@ namespace MediaBrowser.Api.UserLibrary
/// The library manager.
/// The user data repository.
/// The item repo.
+ /// The session manager.
+ /// The dto service.
/// jsonSerializer
public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, ISessionManager sessionManager, IDtoService dtoService)
{
@@ -640,19 +663,11 @@ namespace MediaBrowser.Api.UserLibrary
private SessionInfo GetSession()
{
- var auth = RequestFilterAttribute.GetAuthorization(RequestContext);
+ var auth = AuthorizationRequestFilterAttribute.GetAuthorization(RequestContext);
- string deviceId;
- string client;
- string version;
-
- auth.TryGetValue("DeviceId", out deviceId);
- auth.TryGetValue("Client", out client);
- auth.TryGetValue("Version", out version);
-
- return _sessionManager.Sessions.First(i => string.Equals(i.DeviceId, deviceId) &&
- string.Equals(i.Client, client) &&
- string.Equals(i.ApplicationVersion, version));
+ return _sessionManager.Sessions.First(i => string.Equals(i.DeviceId, auth.DeviceId) &&
+ string.Equals(i.Client, auth.Client) &&
+ string.Equals(i.ApplicationVersion, auth.Version));
}
///
@@ -665,7 +680,17 @@ namespace MediaBrowser.Api.UserLibrary
var item = _dtoService.GetItemByDtoId(request.Id, user.Id);
- _sessionManager.OnPlaybackStart(item, GetSession().Id);
+ var queueableMediaTypes = (request.QueueableMediaTypes ?? string.Empty);
+
+ var info = new PlaybackInfo
+ {
+ CanSeek = request.CanSeek,
+ Item = item,
+ SessionId = GetSession().Id,
+ QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
+ };
+
+ _sessionManager.OnPlaybackStart(info);
}
///
@@ -693,7 +718,12 @@ namespace MediaBrowser.Api.UserLibrary
var item = _dtoService.GetItemByDtoId(request.Id, user.Id);
- var task = _sessionManager.OnPlaybackStopped(item, request.PositionTicks, GetSession().Id);
+ // Kill the encoding
+ ApiEntryPoint.Instance.KillSingleTranscodingJob(item.Path);
+
+ var session = GetSession();
+
+ var task = _sessionManager.OnPlaybackStopped(item, request.PositionTicks, session.Id);
Task.WaitAll(task);
}
diff --git a/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs b/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs
new file mode 100644
index 000000000..39690eb07
--- /dev/null
+++ b/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs
@@ -0,0 +1,87 @@
+using MediaBrowser.Model.IO;
+using SharpCompress.Archive.SevenZip;
+using SharpCompress.Common;
+using SharpCompress.Reader;
+using System.IO;
+
+namespace MediaBrowser.Common.Implementations.Archiving
+{
+ ///
+ /// Class DotNetZipClient
+ ///
+ public class ZipClient : IZipClient
+ {
+ ///
+ /// Extracts all.
+ ///
+ /// The source file.
+ /// The target path.
+ /// if set to true [overwrite existing files].
+ public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
+ {
+ using (var fileStream = File.OpenRead(sourceFile))
+ {
+ ExtractAll(fileStream, targetPath, overwriteExistingFiles);
+ }
+ }
+
+ ///
+ /// Extracts all.
+ ///
+ /// The source.
+ /// The target path.
+ /// if set to true [overwrite existing files].
+ public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles)
+ {
+ using (var reader = ReaderFactory.Open(source))
+ {
+ var options = ExtractOptions.ExtractFullPath;
+
+ if (overwriteExistingFiles)
+ {
+ options = options | ExtractOptions.Overwrite;
+ }
+
+ reader.WriteAllToDirectory(targetPath, options);
+ }
+ }
+
+ ///
+ /// Extracts all from7z.
+ ///
+ /// The source file.
+ /// The target path.
+ /// if set to true [overwrite existing files].
+ public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
+ {
+ using (var fileStream = File.OpenRead(sourceFile))
+ {
+ ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
+ }
+ }
+
+ ///
+ /// Extracts all from7z.
+ ///
+ /// The source.
+ /// The target path.
+ /// if set to true [overwrite existing files].
+ public void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles)
+ {
+ using (var archive = SevenZipArchive.Open(source))
+ {
+ using (var reader = archive.ExtractAllEntries())
+ {
+ var options = ExtractOptions.ExtractFullPath;
+
+ if (overwriteExistingFiles)
+ {
+ options = options | ExtractOptions.Overwrite;
+ }
+
+ reader.WriteAllToDirectory(targetPath, options);
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
index c0ac6a4b3..0d96df9a2 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Implementations.Archiving;
using MediaBrowser.Common.Implementations.NetworkManagement;
using MediaBrowser.Common.Implementations.ScheduledTasks;
using MediaBrowser.Common.Implementations.Security;
@@ -10,6 +11,7 @@ using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
@@ -149,6 +151,12 @@ namespace MediaBrowser.Common.Implementations
/// The installation manager.
protected IInstallationManager InstallationManager { get; set; }
+ ///
+ /// Gets or sets the zip client.
+ ///
+ /// The zip client.
+ protected IZipClient ZipClient { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -202,12 +210,27 @@ namespace MediaBrowser.Common.Implementations
{
Resolve().AddTasks(GetExports(false));
- Task.Run(() => ConfigureAutoRunAtStartup());
+ Task.Run(() => ConfigureAutorun());
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
});
}
+ ///
+ /// Configures the autorun.
+ ///
+ private void ConfigureAutorun()
+ {
+ try
+ {
+ ConfigureAutoRunAtStartup(ConfigurationManager.CommonConfiguration.RunAtStartup);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error configuring autorun", ex);
+ }
+ }
+
///
/// Gets the composable part assemblies.
///
@@ -281,6 +304,9 @@ namespace MediaBrowser.Common.Implementations
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, NetworkManager, ConfigurationManager);
RegisterSingleInstance(InstallationManager);
+
+ ZipClient = new ZipClient();
+ RegisterSingleInstance(ZipClient);
});
}
@@ -453,11 +479,6 @@ namespace MediaBrowser.Common.Implementations
}
}
- ///
- /// Defines the full path to our shortcut in the start menu
- ///
- protected abstract string ProductShortcutPath { get; }
-
///
/// Handles the ConfigurationUpdated event of the ConfigurationManager control.
///
@@ -466,32 +487,10 @@ namespace MediaBrowser.Common.Implementations
///
protected virtual void OnConfigurationUpdated(object sender, EventArgs e)
{
- ConfigureAutoRunAtStartup();
+ ConfigureAutorun();
}
- ///
- /// Configures the auto run at startup.
- ///
- private void ConfigureAutoRunAtStartup()
- {
- if (ConfigurationManager.CommonConfiguration.RunAtStartup)
- {
- //Copy our shortut into the startup folder for this user
- File.Copy(ProductShortcutPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(ProductShortcutPath) ?? "MBstartup.lnk"), true);
- }
- else
- {
- //Remove our shortcut from the startup folder for this user
- try
- {
- File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(ProductShortcutPath) ?? "MBstartup.lnk"));
- }
- catch (FileNotFoundException)
- {
- //This is okay - trying to remove it anyway
- }
- }
- }
+ protected abstract void ConfigureAutoRunAtStartup(bool autorun);
///
/// Removes the plugin.
diff --git a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs
index 109e85d80..e20f9bc13 100644
--- a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs
+++ b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs
@@ -5,7 +5,6 @@ using NLog.Targets;
using System;
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
namespace MediaBrowser.Common.Implementations.Logging
{
@@ -193,17 +192,14 @@ namespace MediaBrowser.Common.Implementations.Logging
if (LoggerLoaded != null)
{
- Task.Run(() =>
+ try
{
- try
- {
- LoggerLoaded(this, EventArgs.Empty);
- }
- catch (Exception ex)
- {
- GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
- }
- });
+ LoggerLoaded(this, EventArgs.Empty);
+ }
+ catch (Exception ex)
+ {
+ GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
+ }
}
}
}
diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
index a96f2c354..11da950f7 100644
--- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
+++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -35,17 +37,8 @@
Always
-
- False
- ..\packages\NLog.2.0.1.2\lib\net45\NLog.dll
-
-
- False
- ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
-
-
- False
- ..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll
+
+ ..\packages\sharpcompress.0.10.1.3\lib\net40\SharpCompress.dll
@@ -54,11 +47,21 @@
+
+ ..\packages\NLog.2.0.1.2\lib\net45\NLog.dll
+
+
+ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
+
+
+ ..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll
+
Properties\SharedVersion.cs
+
@@ -88,11 +91,11 @@
- {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
MediaBrowser.Common
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
index 8278c8a28..6605432fa 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
@@ -1,5 +1,4 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Model.Logging;
@@ -8,6 +7,7 @@ using MediaBrowser.Model.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
@@ -77,6 +77,17 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
QueueScheduledTask();
}
+ ///
+ /// Cancels if running
+ ///
+ ///
+ public void CancelIfRunning()
+ where T : IScheduledTask
+ {
+ var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
+ ((ScheduledTaskWorker)task).CancelIfRunning();
+ }
+
///
/// Queues the scheduled task.
///
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 15f955723..bfd626adb 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -54,33 +54,32 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// Task.
public Task Execute(CancellationToken cancellationToken, IProgress progress)
{
- return Task.Run(() =>
+ // Delete log files more than n days old
+ var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
+
+ var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
+ .Where(f => f.LastWriteTimeUtc < minDateModified)
+ .ToList();
+
+ var index = 0;
+
+ foreach (var file in filesToDelete)
{
- // Delete log files more than n days old
- var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
+ double percent = index;
+ percent /= filesToDelete.Count;
- var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
- .Where(f => f.LastWriteTimeUtc < minDateModified)
- .ToList();
+ progress.Report(100 * percent);
- var index = 0;
+ cancellationToken.ThrowIfCancellationRequested();
- foreach (var file in filesToDelete)
- {
- double percent = index;
- percent /= filesToDelete.Count;
+ File.Delete(file.FullName);
- progress.Report(100 * percent);
+ index++;
+ }
- cancellationToken.ThrowIfCancellationRequested();
+ progress.Report(100);
- File.Delete(file.FullName);
-
- index++;
- }
-
- progress.Report(100);
- });
+ return Task.FromResult(true);
}
///
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs
index e860834ec..00928255c 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs
@@ -58,7 +58,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
progress.Report(0);
- return Task.Run(() => LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info));
+ LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
+ ? LogSeverity.Debug
+ : LogSeverity.Info);
+
+ return Task.FromResult(true);
}
///
diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config
index 4be861cce..d03cb14e0 100644
--- a/MediaBrowser.Common.Implementations/packages.config
+++ b/MediaBrowser.Common.Implementations/packages.config
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 1611c55da..8acd1a83c 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -32,26 +34,19 @@
prompt
4
-
-
-
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
-
-
- False
- ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
-
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
+
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
+
+
+ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
+
@@ -113,7 +108,7 @@
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs
index ec0e7c1c9..394872783 100644
--- a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs
+++ b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs
@@ -21,6 +21,13 @@ namespace MediaBrowser.Common.ScheduledTasks
void CancelIfRunningAndQueue()
where T : IScheduledTask;
+ ///
+ /// Cancels if running.
+ ///
+ ///
+ void CancelIfRunning()
+ where T : IScheduledTask;
+
///
/// Queues the scheduled task.
///
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 0d91a2e86..0f090f587 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -16,7 +16,6 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using MoreLinq;
namespace MediaBrowser.Controller.Entities
{
@@ -171,6 +170,25 @@ namespace MediaBrowser.Controller.Entities
return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
}
+ ///
+ /// Clears the children.
+ ///
+ /// The cancellation token.
+ /// Task.
+ public Task ClearChildren(CancellationToken cancellationToken)
+ {
+ var items = ActualChildren.ToList();
+
+ ClearChildrenInternal();
+
+ foreach (var item in items)
+ {
+ LibraryManager.ReportItemRemoved(item);
+ }
+
+ return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
+ }
+
#region Indexing
///
@@ -671,7 +689,7 @@ namespace MediaBrowser.Controller.Entities
var options = new ParallelOptions
{
- MaxDegreeOfParallelism = 20
+ MaxDegreeOfParallelism = 10
};
Parallel.ForEach(nonCachedChildren, options, child =>
@@ -733,6 +751,11 @@ namespace MediaBrowser.Controller.Entities
if (actualRemovals.Count > 0)
{
RemoveChildrenInternal(actualRemovals);
+
+ foreach (var item in actualRemovals)
+ {
+ LibraryManager.ReportItemRemoved(item);
+ }
}
await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
@@ -781,7 +804,7 @@ namespace MediaBrowser.Controller.Entities
foreach (var tuple in list)
{
- if (tasks.Count > 8)
+ if (tasks.Count > 5)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index f49bd8cf0..0b27a350b 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -42,6 +44,8 @@
x86
prompt
MinimumRecommendedRules.ruleset
+ 4
+ false
bin\x86\Release\
@@ -51,12 +55,9 @@
x86
prompt
MinimumRecommendedRules.ruleset
+ 4
-
- False
- ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
-
@@ -66,6 +67,9 @@
+
+ ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
+
@@ -165,6 +169,8 @@
+
+
@@ -172,11 +178,11 @@
- {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
MediaBrowser.Common
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
diff --git a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
index a8dc8788f..2364debed 100644
--- a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
@@ -385,7 +385,7 @@ namespace MediaBrowser.Controller.Providers
var sb = new StringBuilder();
var extensions = FileStampExtensionsDictionary;
- var numExtensions = extensions.Count;
+ var numExtensions = FilestampExtensions.Length;
// Record the name of each file
// Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 1976c653a..0932ee52d 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -1,7 +1,9 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Session
@@ -11,6 +13,12 @@ namespace MediaBrowser.Controller.Session
///
public interface ISessionManager
{
+ ///
+ /// Adds the parts.
+ ///
+ /// The remote controllers.
+ void AddParts(IEnumerable remoteControllers);
+
///
/// Occurs when [playback start].
///
@@ -47,11 +55,9 @@ namespace MediaBrowser.Controller.Session
///
/// Used to report that playback has started for an item
///
- /// The item.
- /// The session id.
+ /// The info.
/// Task.
- ///
- Task OnPlaybackStart(BaseItem item, Guid sessionId);
+ Task OnPlaybackStart(PlaybackInfo info);
///
/// Used to report playback progress for an item
@@ -59,6 +65,7 @@ namespace MediaBrowser.Controller.Session
/// The item.
/// The position ticks.
/// if set to true [is paused].
+ /// if set to true [is muted].
/// The session id.
/// Task.
///
@@ -73,5 +80,50 @@ namespace MediaBrowser.Controller.Session
/// Task.
///
Task OnPlaybackStopped(BaseItem item, long? positionTicks, Guid sessionId);
+
+ ///
+ /// Sends the system command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendSystemCommand(Guid sessionId, SystemCommand command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the message command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendMessageCommand(Guid sessionId, MessageCommand command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the play command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendPlayCommand(Guid sessionId, PlayRequest command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the browse command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendBrowseCommand(Guid sessionId, BrowseRequest command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the playstate command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendPlaystateCommand(Guid sessionId, PlaystateRequest command, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Session/ISessionRemoteController.cs b/MediaBrowser.Controller/Session/ISessionRemoteController.cs
new file mode 100644
index 000000000..9ba5c983d
--- /dev/null
+++ b/MediaBrowser.Controller/Session/ISessionRemoteController.cs
@@ -0,0 +1,61 @@
+using MediaBrowser.Model.Session;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Session
+{
+ public interface ISessionRemoteController
+ {
+ ///
+ /// Supportses the specified session.
+ ///
+ /// The session.
+ /// true if XXXX, false otherwise
+ bool Supports(SessionInfo session);
+
+ ///
+ /// Sends the system command.
+ ///
+ /// The session.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the message command.
+ ///
+ /// The session.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the play command.
+ ///
+ /// The session.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the browse command.
+ ///
+ /// The session.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the playstate command.
+ ///
+ /// The session.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/Session/PlaybackInfo.cs b/MediaBrowser.Controller/Session/PlaybackInfo.cs
new file mode 100644
index 000000000..ab3111e76
--- /dev/null
+++ b/MediaBrowser.Controller/Session/PlaybackInfo.cs
@@ -0,0 +1,38 @@
+using MediaBrowser.Controller.Entities;
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Session
+{
+ public class PlaybackInfo
+ {
+ public PlaybackInfo()
+ {
+ QueueableMediaTypes = new List();
+ }
+
+ ///
+ /// Gets or sets a value indicating whether this instance can seek.
+ ///
+ /// true if this instance can seek; otherwise, false.
+ public bool CanSeek { get; set; }
+
+ ///
+ /// Gets or sets the queueable media types.
+ ///
+ /// The queueable media types.
+ public List QueueableMediaTypes { get; set; }
+
+ ///
+ /// Gets or sets the item.
+ ///
+ /// The item.
+ public BaseItem Item { get; set; }
+
+ ///
+ /// Gets or sets the session id.
+ ///
+ /// The session id.
+ public Guid SessionId { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 6c0f1a085..dc934b70a 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -15,8 +15,21 @@ namespace MediaBrowser.Controller.Session
public SessionInfo()
{
WebSockets = new List();
+ QueueableMediaTypes = new List();
}
+ ///
+ /// Gets or sets a value indicating whether this instance can seek.
+ ///
+ /// true if this instance can seek; otherwise, false.
+ public bool CanSeek { get; set; }
+
+ ///
+ /// Gets or sets the queueable media types.
+ ///
+ /// The queueable media types.
+ public List QueueableMediaTypes { get; set; }
+
///
/// Gets or sets the id.
///
@@ -70,7 +83,7 @@ namespace MediaBrowser.Controller.Session
///
/// The name of the now viewing item.
public string NowViewingItemName { get; set; }
-
+
///
/// Gets or sets the now playing item.
///
@@ -94,7 +107,7 @@ namespace MediaBrowser.Controller.Session
///
/// true if this instance is muted; otherwise, false.
public bool IsMuted { get; set; }
-
+
///
/// Gets or sets the device id.
///
@@ -126,7 +139,7 @@ namespace MediaBrowser.Controller.Session
return WebSockets.Any(i => i.State == WebSocketState.Open);
}
- return (DateTime.UtcNow - LastActivityDate).TotalMinutes <= 5;
+ return (DateTime.UtcNow - LastActivityDate).TotalMinutes <= 10;
}
}
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 03ea79b3b..4a459cac8 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -497,9 +497,11 @@ namespace MediaBrowser.Model.ApiClient
///
/// The item id.
/// The user id.
+ /// if set to true [is seekable].
+ /// The list of media types that the client is capable of queuing onto the playlist. See MediaType class.
/// Task{UserItemDataDto}.
/// itemId
- Task ReportPlaybackStartAsync(string itemId, string userId);
+ Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List queueableMediaTypes);
///
/// Reports playback progress to the server
diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs
index c9e7e0db6..694c393aa 100644
--- a/MediaBrowser.Model/IO/IZipClient.cs
+++ b/MediaBrowser.Model/IO/IZipClient.cs
@@ -22,5 +22,21 @@ namespace MediaBrowser.Model.IO
/// The target path.
/// if set to true [overwrite existing files].
void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles);
+
+ ///
+ /// Extracts all from7z.
+ ///
+ /// The source file.
+ /// The target path.
+ /// if set to true [overwrite existing files].
+ void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles);
+
+ ///
+ /// Extracts all from7z.
+ ///
+ /// The source.
+ /// The target path.
+ /// if set to true [overwrite existing files].
+ void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles);
}
}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 8fb471c2d..fa4fc2986 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -14,6 +14,8 @@
..\
true
..\packages\Fody.1.17.0.0
+ 10.0.0
+ 2.0
true
@@ -162,14 +164,13 @@
-
- False
- ..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll
- False
-
+
+ ..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll
+ False
+
diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs
index f9b0e0abd..02b7f0226 100644
--- a/MediaBrowser.Model/Session/SessionInfoDto.cs
+++ b/MediaBrowser.Model/Session/SessionInfoDto.cs
@@ -1,11 +1,24 @@
-using System.ComponentModel;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
using System;
+using System.Collections.Generic;
+using System.ComponentModel;
namespace MediaBrowser.Model.Session
{
public class SessionInfoDto : INotifyPropertyChanged
{
+ ///
+ /// Gets or sets a value indicating whether this instance can seek.
+ ///
+ /// true if this instance can seek; otherwise, false.
+ public bool CanSeek { get; set; }
+
+ ///
+ /// Gets or sets the queueable media types.
+ ///
+ /// The queueable media types.
+ public List QueueableMediaTypes { get; set; }
+
///
/// Gets or sets the id.
///
diff --git a/MediaBrowser.Mono.sln b/MediaBrowser.Mono.sln
new file mode 100644
index 000000000..0dc78ca2a
--- /dev/null
+++ b/MediaBrowser.Mono.sln
@@ -0,0 +1,68 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Mono", "MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj", "{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.ActiveCfg = Debug|x86
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.Build.0 = Debug|x86
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.ActiveCfg = Release|x86
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.Build.0 = Release|x86
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.Build.0 = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.Build.0 = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.Build.0 = Debug|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.ActiveCfg = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.Build.0 = Release|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.Build.0 = Debug|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x86.ActiveCfg = Release|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x86.Build.0 = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.Build.0 = Debug|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.ActiveCfg = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.Build.0 = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.Build.0 = Debug|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.ActiveCfg = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.Build.0 = Release|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.Build.0 = Debug|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.ActiveCfg = Release|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.Build.0 = Release|Any CPU
+ {A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Debug|x86.ActiveCfg = Debug|x86
+ {A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Debug|x86.Build.0 = Debug|x86
+ {A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Release|x86.ActiveCfg = Release|x86
+ {A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Release|x86.Build.0 = Release|x86
+ {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.Build.0 = Debug|Any CPU
+ {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
+ {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj
+ EndGlobalSection
+EndGlobal
diff --git a/MediaBrowser.Mono.userprefs b/MediaBrowser.Mono.userprefs
new file mode 100644
index 000000000..80da5915d
--- /dev/null
+++ b/MediaBrowser.Mono.userprefs
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 139c622fc..ef94d77d1 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -32,10 +34,6 @@
4
-
- False
- ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
-
@@ -44,6 +42,9 @@
+
+ ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
+
@@ -116,15 +117,15 @@
- {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
MediaBrowser.Common
- {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}
MediaBrowser.Controller
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs
index 690c9b3ff..c28d06cbb 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs
@@ -178,6 +178,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
+ SetLastRefreshed(item, DateTime.UtcNow);
return true;
}
diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
index fefcd8371..adc013699 100644
--- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
diff --git a/MediaBrowser.Providers/Music/SoundtrackPostScanTask.cs b/MediaBrowser.Providers/Music/SoundtrackPostScanTask.cs
index 18868d3ea..e18351248 100644
--- a/MediaBrowser.Providers/Music/SoundtrackPostScanTask.cs
+++ b/MediaBrowser.Providers/Music/SoundtrackPostScanTask.cs
@@ -23,7 +23,9 @@ namespace MediaBrowser.Providers.Music
public Task Run(IProgress progress, CancellationToken cancellationToken)
{
- return Task.Run(() => RunInternal(progress, cancellationToken));
+ RunInternal(progress, cancellationToken);
+
+ return Task.FromResult(true);
}
private void RunInternal(IProgress progress, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs
index e6195c03e..bd63b5fbd 100644
--- a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -58,7 +59,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List { });
// Set last refreshed so that the provider doesn't trigger after the file save
PersonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
diff --git a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs
index 795e824fc..a27fb7363 100644
--- a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -70,7 +71,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List { });
// Set last refreshed so that the provider doesn't trigger after the file save
ArtistProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
diff --git a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs b/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs
index f5fc37fe7..f09b34570 100644
--- a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
@@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List { });
BoxSetProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
}
diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs
index d90cb94c2..854c508b9 100644
--- a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -87,7 +88,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new[]
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List
{
"FirstAired",
"SeasonNumber",
diff --git a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs
index 23339ec75..6e95cc8c5 100644
--- a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -77,7 +78,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List { });
FolderProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
}
diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.Providers/Savers/GameXmlSaver.cs
index 41bd364c8..1e654f72f 100644
--- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/GameXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Providers.Movies;
@@ -66,7 +67,7 @@ namespace MediaBrowser.Providers.Savers
if (!string.IsNullOrEmpty(game.GameSystem))
{
- builder.Append("");
+ builder.Append("" + SecurityElement.Escape(game.GameSystem) + "");
}
XmlSaverHelpers.AddCommonNodes(item, builder);
@@ -75,7 +76,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new[]
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List
{
"Players",
"GameSystem"
diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
index 2402bcd7f..761bcefd1 100644
--- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
@@ -103,7 +104,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new[]
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List
{
"IMDBrating",
"Description",
diff --git a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs
index 1b1377ac8..92f6db29b 100644
--- a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs
@@ -1,4 +1,5 @@
-using System.Security;
+using System.Collections.Generic;
+using System.Security;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Providers.Movies;
@@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new[]
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List
{
"PlaceOfBirth"
});
diff --git a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs b/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs
index 97e8b671f..e484b3d39 100644
--- a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List { });
SeasonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
}
diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs
index 4e2f538c7..a4ff9c7d8 100644
--- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -105,7 +106,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new[]
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List
{
"id",
"SeriesName",
@@ -113,7 +114,10 @@ namespace MediaBrowser.Providers.Savers
"Network",
"Airs_Time",
"Airs_DayOfWeek",
- "FirstAired"
+ "FirstAired",
+
+ // Don't preserve old series node
+ "Series"
});
// Set last refreshed so that the provider doesn't trigger after the file save
diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
index cea7cf926..338447c10 100644
--- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
+++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
@@ -29,13 +29,11 @@ namespace MediaBrowser.Providers.Savers
/// The XML.
/// The path.
/// The XML tags used.
- public static void Save(StringBuilder xml, string path, IEnumerable xmlTagsUsed)
+ public static void Save(StringBuilder xml, string path, List xmlTagsUsed)
{
if (File.Exists(path))
{
- var tags = xmlTagsUsed.ToList();
-
- tags.AddRange(new[]
+ xmlTagsUsed.AddRange(new[]
{
"MediaInfo",
"ContentRating",
@@ -88,7 +86,7 @@ namespace MediaBrowser.Providers.Savers
});
var position = xml.ToString().LastIndexOf("", StringComparison.OrdinalIgnoreCase);
- xml.Insert(position, GetCustomTags(path, tags));
+ xml.Insert(position, GetCustomTags(path, xmlTagsUsed));
}
var xmlDocument = new XmlDocument();
@@ -142,17 +140,46 @@ namespace MediaBrowser.Providers.Savers
/// The path.
/// The XML tags used.
/// System.String.
- private static string GetCustomTags(string path, ICollection xmlTagsUsed)
+ private static string GetCustomTags(string path, IEnumerable xmlTagsUsed)
{
- var doc = new XmlDocument();
- doc.Load(path);
+ var settings = new XmlReaderSettings
+ {
+ CheckCharacters = false,
+ IgnoreProcessingInstructions = true,
+ IgnoreComments = true,
+ ValidationType = ValidationType.None
+ };
- var nodes = doc.DocumentElement.ChildNodes.Cast()
- .Where(i => !xmlTagsUsed.Contains(i.Name))
- .Select(i => i.OuterXml)
- .ToArray();
+ var tagsDictionary = xmlTagsUsed.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
- return string.Join(Environment.NewLine, nodes);
+ var builder = new StringBuilder();
+
+ using (var streamReader = new StreamReader(path, Encoding.UTF8))
+ {
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
+ {
+ reader.MoveToContent();
+
+ // Loop through each element
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ if (!tagsDictionary.ContainsKey(reader.Name))
+ {
+ builder.AppendLine(reader.ReadOuterXml());
+ }
+ else
+ {
+ reader.Skip();
+ }
+ }
+ }
+ }
+ }
+
+ return builder.ToString();
}
///
diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
index a781551de..2b73ba1f7 100644
--- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
+++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
@@ -21,7 +21,9 @@ namespace MediaBrowser.Providers.TV
public Task Run(IProgress progress, CancellationToken cancellationToken)
{
- return Task.Run(() => RunInternal(progress, cancellationToken));
+ RunInternal(progress, cancellationToken);
+
+ return Task.FromResult(true);
}
private void RunInternal(IProgress progress, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
index 1f7361d2f..305bede56 100644
--- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
+++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
@@ -121,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
}
catch (IOException)
{
- // Cache file doesn't exist or is currently being written ro
+ // Cache file doesn't exist or is currently being written to
}
var semaphore = GetLock(cacheFilePath);
@@ -129,21 +129,24 @@ namespace MediaBrowser.Server.Implementations.Drawing
await semaphore.WaitAsync().ConfigureAwait(false);
// Check again in case of lock contention
- if (File.Exists(cacheFilePath))
+ try
{
- try
- {
- using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
- {
- await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
- return;
- }
- }
- finally
+ using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
+ await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
semaphore.Release();
+ return;
}
}
+ catch (IOException)
+ {
+ // Cache file doesn't exist or is currently being written to
+ }
+ catch
+ {
+ semaphore.Release();
+ throw;
+ }
try
{
@@ -188,12 +191,10 @@ namespace MediaBrowser.Server.Implementations.Drawing
var bytes = outputMemoryStream.ToArray();
- var outputTask = toStream.WriteAsync(bytes, 0, bytes.Length);
+ await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
// kick off a task to cache the result
- var cacheTask = CacheResizedImage(cacheFilePath, bytes);
-
- await Task.WhenAll(outputTask, cacheTask).ConfigureAwait(false);
+ CacheResizedImage(cacheFilePath, bytes, semaphore);
}
}
}
@@ -202,12 +203,51 @@ namespace MediaBrowser.Server.Implementations.Drawing
}
}
}
- finally
+ catch
{
semaphore.Release();
+
+ throw;
}
}
+ ///
+ /// Caches the resized image.
+ ///
+ /// The cache file path.
+ /// The bytes.
+ /// The semaphore.
+ private void CacheResizedImage(string cacheFilePath, byte[] bytes, SemaphoreSlim semaphore)
+ {
+ Task.Run(async () =>
+ {
+ try
+ {
+ var parentPath = Path.GetDirectoryName(cacheFilePath);
+
+ if (!Directory.Exists(parentPath))
+ {
+ Directory.CreateDirectory(parentPath);
+ }
+
+ // Save to the cache location
+ using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
+ {
+ // Save to the filestream
+ await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath);
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ });
+ }
+
///
/// Sets the color of the background.
///
@@ -363,28 +403,6 @@ namespace MediaBrowser.Server.Implementations.Drawing
return croppedImagePath;
}
- ///
- /// Caches the resized image.
- ///
- /// The cache file path.
- /// The bytes.
- private async Task CacheResizedImage(string cacheFilePath, byte[] bytes)
- {
- var parentPath = Path.GetDirectoryName(cacheFilePath);
-
- if (!Directory.Exists(parentPath))
- {
- Directory.CreateDirectory(parentPath);
- }
-
- // Save to the cache location
- using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
- {
- // Save to the filestream
- await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
- }
- }
-
///
/// Gets the cache file path based on a set of parameters
///
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index e5260004a..a5f54b938 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -237,7 +237,9 @@ namespace MediaBrowser.Server.Implementations.Dto
NowViewingItemId = session.NowViewingItemId,
NowViewingItemName = session.NowViewingItemName,
NowViewingItemType = session.NowViewingItemType,
- ApplicationVersion = session.ApplicationVersion
+ ApplicationVersion = session.ApplicationVersion,
+ CanSeek = session.CanSeek,
+ QueueableMediaTypes = session.QueueableMediaTypes
};
if (session.NowPlayingItem != null)
diff --git a/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
similarity index 95%
rename from MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs
rename to MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 595d5c89f..9c1a953b1 100644
--- a/MediaBrowser.ServerApplication/EntryPoints/UdpServerEntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.Udp;
using System.Net.Sockets;
-namespace MediaBrowser.ServerApplication.EntryPoints
+namespace MediaBrowser.Server.Implementations.EntryPoints
{
///
/// Class UdpServerEntryPoint
@@ -35,6 +35,8 @@ namespace MediaBrowser.ServerApplication.EntryPoints
///
private readonly IHttpServer _httpServer;
+ public const int PortNumber = 7359;
+
///
/// Initializes a new instance of the class.
///
@@ -59,7 +61,7 @@ namespace MediaBrowser.ServerApplication.EntryPoints
try
{
- udpServer.Start(ApplicationHost.UdpServerPort);
+ udpServer.Start(PortNumber);
UdpServer = udpServer;
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs
index f6547dec1..4f795fdd5 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs
@@ -314,6 +314,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// The CTX.
private async void ProcessHttpRequestAsync(HttpListenerContext context)
{
+ var date = DateTime.Now;
+
LogHttpRequest(context);
if (context.Request.IsWebSocketRequest)
@@ -360,7 +362,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
var url = context.Request.Url.ToString();
var endPoint = context.Request.RemoteEndPoint;
- LogResponse(context, url, endPoint);
+ var duration = DateTime.Now - date;
+
+ LogResponse(context, url, endPoint, duration);
}
catch (Exception ex)
@@ -461,14 +465,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// The CTX.
/// The URL.
/// The end point.
- private void LogResponse(HttpListenerContext ctx, string url, IPEndPoint endPoint)
+ /// The duration.
+ private void LogResponse(HttpListenerContext ctx, string url, IPEndPoint endPoint, TimeSpan duration)
{
if (!EnableHttpRequestLogging)
{
return;
}
- var statusode = ctx.Response.StatusCode;
+ var statusCode = ctx.Response.StatusCode;
var log = new StringBuilder();
@@ -476,7 +481,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
log.AppendLine("Headers: " + string.Join(",", ctx.Response.Headers.AllKeys.Select(k => k + "=" + ctx.Response.Headers[k])));
- var msg = "Http Response Sent (" + statusode + ") to " + endPoint;
+ var responseTime = string.Format(". Response time: {0} ms", duration.TotalMilliseconds);
+
+ var msg = "Response code " + statusCode + " sent to " + endPoint + responseTime;
_logger.LogMultiline(msg, LogSeverity.Debug, log);
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index a5b792726..1bc3f1094 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -829,10 +829,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// Task.
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress progress)
{
- const int maxTasks = 3;
-
- var tasks = new List();
-
var people = RootFolder.RecursiveChildren
.SelectMany(c => c.People)
.DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
@@ -842,47 +838,27 @@ namespace MediaBrowser.Server.Implementations.Library
foreach (var person in people)
{
- if (tasks.Count > maxTasks)
- {
- await Task.WhenAll(tasks).ConfigureAwait(false);
- tasks.Clear();
+ cancellationToken.ThrowIfCancellationRequested();
- // Safe cancellation point, when there are no pending tasks
- cancellationToken.ThrowIfCancellationRequested();
+ try
+ {
+ var item = GetPerson(person.Name);
+
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ }
+ catch (IOException ex)
+ {
+ _logger.ErrorException("Error validating IBN entry {0}", ex, person.Name);
}
- // Avoid accessing the foreach variable within the closure
- var currentPerson = person;
+ // Update progress
+ numComplete++;
+ double percent = numComplete;
+ percent /= people.Count;
- tasks.Add(Task.Run(async () =>
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- var item = GetPerson(currentPerson.Name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error validating IBN entry {0}", ex, currentPerson.Name);
- }
-
- // Update progress
- lock (progress)
- {
- numComplete++;
- double percent = numComplete;
- percent /= people.Count;
-
- progress.Report(100 * percent);
- }
- }));
+ progress.Report(100 * percent);
}
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
progress.Report(100);
_logger.Info("People validation complete");
@@ -956,7 +932,9 @@ namespace MediaBrowser.Server.Implementations.Library
public Task ValidateMediaLibrary(IProgress progress, CancellationToken cancellationToken)
{
// Just run the scheduled task so that the user can see it
- return Task.Run(() => _taskManager.CancelIfRunningAndQueue());
+ _taskManager.CancelIfRunningAndQueue();
+
+ return Task.FromResult(true);
}
///
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
index eb89210ff..b9e033d23 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
@@ -41,8 +41,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// Task.
public async Task Run(IProgress progress, CancellationToken cancellationToken)
{
- var allItems = _libraryManager.RootFolder.RecursiveChildren.OfType().ToList();
-
var userLibraries = _userManager.Users
.Select(i => new Tuple>(i.Id, i.RootFolder.GetRecursiveChildren(i).OfType().ToList()))
.ToList();
@@ -79,6 +77,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
+ catch (OperationCanceledException)
+ {
+ // Don't clutter the log
+ }
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
index 9a34dd1b0..e4d989c33 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -42,16 +42,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// Task.
public async Task Run(IProgress progress, CancellationToken cancellationToken)
{
- var allItems = _libraryManager.RootFolder.RecursiveChildren
- .Where(i => !(i is IHasMusicGenres) && !(i is Game))
- .ToList();
-
var userLibraries = _userManager.Users
.Select(i => new Tuple>(i.Id, i.RootFolder.GetRecursiveChildren(i).Where(m => !(m is IHasMusicGenres) && !(m is Game)).ToList()))
.ToList();
- var allLibraryItems = allItems;
-
var masterDictionary = new Dictionary>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
@@ -84,6 +78,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
+ catch (OperationCanceledException)
+ {
+ // Don't clutter the log
+ }
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
index 1b211d5f4..1edc24762 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
@@ -42,16 +42,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// Task.
public async Task Run(IProgress progress, CancellationToken cancellationToken)
{
- var allItems = _libraryManager.RootFolder.RecursiveChildren
- .Where(i => i is IHasMusicGenres)
- .ToList();
-
var userLibraries = _userManager.Users
.Select(i => new Tuple>(i.Id, i.RootFolder.GetRecursiveChildren(i).Where(m => m is IHasMusicGenres).ToList()))
.ToList();
- var allLibraryItems = allItems;
-
var masterDictionary = new Dictionary>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
@@ -84,6 +78,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
+ catch (OperationCanceledException)
+ {
+ // Don't clutter the log
+ }
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs
index efefaeba3..dc96632f6 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs
@@ -41,7 +41,9 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// Task.
public Task Run(IProgress progress, CancellationToken cancellationToken)
{
- return Task.Run(() => RunInternal(progress, cancellationToken));
+ RunInternal(progress, cancellationToken);
+
+ return Task.FromResult(true);
}
private void RunInternal(IProgress progress, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
index a4d880329..05689f8e5 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -41,14 +41,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// Task.
public async Task Run(IProgress progress, CancellationToken cancellationToken)
{
- var allItems = _libraryManager.RootFolder.RecursiveChildren.ToList();
-
var userLibraries = _userManager.Users
.Select(i => new Tuple>(i.Id, i.RootFolder.GetRecursiveChildren(i).ToList()))
.ToList();
- var allLibraryItems = allItems;
-
var masterDictionary = new Dictionary>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
@@ -81,6 +77,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
+ catch (OperationCanceledException)
+ {
+ // Don't clutter the log
+ }
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 9d2fc8c6b..f409b7205 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -35,62 +37,14 @@
..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll
-
- False
- ..\packages\MediaBrowser.BdInfo.1.0.0.2\lib\net45\BdInfo.dll
-
..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll
..\packages\Lucene.Net.3.0.3\lib\NET40\Lucene.Net.dll
-
- False
- ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
-
-
- False
- ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.dll
-
-
- False
- ..\packages\ServiceStack.Api.Swagger.3.9.59\lib\net35\ServiceStack.Api.Swagger.dll
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
-
-
- False
- ..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll
-
-
- False
- ..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll
-
-
- False
- ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.ServiceInterface.dll
-
-
- False
- ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
-
-
- False
- ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.dll
-
-
- False
- ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll
-
..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll
@@ -106,6 +60,42 @@
+
+ ..\packages\MediaBrowser.BdInfo.1.0.0.2\lib\net45\BdInfo.dll
+
+
+ ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
+
+
+ ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.dll
+
+
+ ..\packages\ServiceStack.Api.Swagger.3.9.59\lib\net35\ServiceStack.Api.Swagger.dll
+
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
+
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
+
+
+ ..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll
+
+
+ ..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll
+
+
+ ..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.ServiceInterface.dll
+
+
+ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
+
+
+ ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.dll
+
+
+ ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll
+
@@ -123,6 +113,7 @@
+
@@ -183,6 +174,7 @@
Code
+
@@ -222,19 +214,19 @@
- {c4d2573a-3fd3-441f-81af-174ac4cd4e1d}
+ {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}
MediaBrowser.Common.Implementations
- {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
MediaBrowser.Common
- {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}
MediaBrowser.Controller
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
index b24c9a5ca..2f353b8c0 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
+++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
@@ -57,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
///
/// The FF probe resource pool
///
- private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2);
+ private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(1, 1);
public string FFMpegPath { get; private set; }
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index f4f5f08e4..9c5cf6f1c 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -333,17 +333,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Task.
public Task SaveCriticReviews(Guid itemId, IEnumerable criticReviews)
{
- return Task.Run(() =>
+ if (!Directory.Exists(_criticReviewsPath))
{
- if (!Directory.Exists(_criticReviewsPath))
- {
- Directory.CreateDirectory(_criticReviewsPath);
- }
+ Directory.CreateDirectory(_criticReviewsPath);
+ }
- var path = Path.Combine(_criticReviewsPath, itemId + ".json");
+ var path = Path.Combine(_criticReviewsPath, itemId + ".json");
- _jsonSerializer.SerializeToFile(criticReviews.ToList(), path);
- });
+ _jsonSerializer.SerializeToFile(criticReviews.ToList(), path);
+
+ return Task.FromResult(true);
}
///
diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
index 608738f7f..d8872f318 100644
--- a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
+++ b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
@@ -102,6 +102,18 @@ namespace MediaBrowser.Server.Implementations.Providers
using (source)
{
+ // If the file is currently hidden we'll have to remove that or the save will fail
+ var file = new FileInfo(path);
+
+ // This will fail if the file is hidden
+ if (file.Exists)
+ {
+ if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+ {
+ file.Attributes &= ~FileAttributes.Hidden;
+ }
+ }
+
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
index d9b88368b..4829dc405 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
+++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
@@ -159,6 +159,10 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
{
previouslyFailedImages = new List();
}
+ catch (DirectoryNotFoundException)
+ {
+ previouslyFailedImages = new List();
+ }
foreach (var video in videos)
{
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index ce757d142..79dfbc8a5 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -6,6 +7,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Session;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -75,6 +77,12 @@ namespace MediaBrowser.Server.Implementations.Session
_userRepository = userRepository;
}
+ private List _remoteControllers;
+ public void AddParts(IEnumerable remoteControllers)
+ {
+ _remoteControllers = remoteControllers.ToList();
+ }
+
///
/// Gets all connections.
///
@@ -122,7 +130,7 @@ namespace MediaBrowser.Server.Implementations.Session
var activityDate = DateTime.UtcNow;
var session = GetSessionInfo(clientType, appVersion, deviceId, deviceName, user);
-
+
session.LastActivityDate = activityDate;
if (user == null)
@@ -207,25 +215,33 @@ namespace MediaBrowser.Server.Implementations.Session
///
/// Used to report that playback has started for an item
///
- /// The item.
- /// The session id.
+ /// The info.
/// Task.
- ///
- public async Task OnPlaybackStart(BaseItem item, Guid sessionId)
+ /// info
+ public async Task OnPlaybackStart(PlaybackInfo info)
{
- if (item == null)
+ if (info == null)
{
- throw new ArgumentNullException();
+ throw new ArgumentNullException("info");
+ }
+ if (info.SessionId == Guid.Empty)
+ {
+ throw new ArgumentNullException("info");
}
- var session = Sessions.First(i => i.Id.Equals(sessionId));
+ var session = Sessions.First(i => i.Id.Equals(info.SessionId));
+
+ var item = info.Item;
UpdateNowPlayingItem(session, item, false, false);
+ session.CanSeek = info.CanSeek;
+ session.QueueableMediaTypes = info.QueueableMediaTypes;
+
var key = item.GetUserDataKey();
var user = session.User;
-
+
var data = _userDataRepository.GetUserData(user.Id, key);
data.PlayCount++;
@@ -313,7 +329,7 @@ namespace MediaBrowser.Server.Implementations.Session
{
throw new ArgumentOutOfRangeException("positionTicks");
}
-
+
var session = Sessions.First(i => i.Id.Equals(sessionId));
RemoveNowPlayingItem(session, item);
@@ -321,7 +337,7 @@ namespace MediaBrowser.Server.Implementations.Session
var key = item.GetUserDataKey();
var user = session.User;
-
+
var data = _userDataRepository.GetUserData(user.Id, key);
if (positionTicks.HasValue)
@@ -400,5 +416,118 @@ namespace MediaBrowser.Server.Implementations.Session
data.PlaybackPositionTicks = positionTicks;
}
+
+ ///
+ /// Gets the session for remote control.
+ ///
+ /// The session id.
+ /// SessionInfo.
+ ///
+ private SessionInfo GetSessionForRemoteControl(Guid sessionId)
+ {
+ var session = Sessions.First(i => i.Id.Equals(sessionId));
+
+ if (session == null)
+ {
+ throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
+ }
+
+ if (!session.SupportsRemoteControl)
+ {
+ throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
+ }
+
+ return session;
+ }
+
+ ///
+ /// Gets the controllers.
+ ///
+ /// The session.
+ /// IEnumerable{ISessionRemoteController}.
+ private IEnumerable GetControllers(SessionInfo session)
+ {
+ return _remoteControllers.Where(i => i.Supports(session));
+ }
+
+ ///
+ /// Sends the system command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ public Task SendSystemCommand(Guid sessionId, SystemCommand command, CancellationToken cancellationToken)
+ {
+ var session = GetSessionForRemoteControl(sessionId);
+
+ var tasks = GetControllers(session).Select(i => i.SendSystemCommand(session, command, cancellationToken));
+
+ return Task.WhenAll(tasks);
+ }
+
+ ///
+ /// Sends the message command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ public Task SendMessageCommand(Guid sessionId, MessageCommand command, CancellationToken cancellationToken)
+ {
+ var session = GetSessionForRemoteControl(sessionId);
+
+ var tasks = GetControllers(session).Select(i => i.SendMessageCommand(session, command, cancellationToken));
+
+ return Task.WhenAll(tasks);
+ }
+
+ ///
+ /// Sends the play command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ public Task SendPlayCommand(Guid sessionId, PlayRequest command, CancellationToken cancellationToken)
+ {
+ var session = GetSessionForRemoteControl(sessionId);
+
+ var tasks = GetControllers(session).Select(i => i.SendPlayCommand(session, command, cancellationToken));
+
+ return Task.WhenAll(tasks);
+ }
+
+ ///
+ /// Sends the browse command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ public Task SendBrowseCommand(Guid sessionId, BrowseRequest command, CancellationToken cancellationToken)
+ {
+ var session = GetSessionForRemoteControl(sessionId);
+
+ var tasks = GetControllers(session).Select(i => i.SendBrowseCommand(session, command, cancellationToken));
+
+ return Task.WhenAll(tasks);
+ }
+
+ ///
+ /// Sends the playstate command.
+ ///
+ /// The session id.
+ /// The command.
+ /// The cancellation token.
+ /// Task.
+ public Task SendPlaystateCommand(Guid sessionId, PlaystateRequest command, CancellationToken cancellationToken)
+ {
+ var session = GetSessionForRemoteControl(sessionId);
+
+ var tasks = GetControllers(session).Select(i => i.SendPlaystateCommand(session, command, cancellationToken));
+
+ return Task.WhenAll(tasks);
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
index 2a4361e61..95eb5948f 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -101,16 +101,7 @@ namespace MediaBrowser.Server.Implementations.Session
}
else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase))
{
- _logger.Debug("Received PlaybackStart message");
-
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
-
- if (session != null && session.User != null)
- {
- var item = _dtoService.GetItemByDtoId(message.Data);
-
- _sessionManager.OnPlaybackStart(item, session.Id);
- }
+ ReportPlaybackStart(message);
}
else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase))
{
@@ -170,5 +161,46 @@ namespace MediaBrowser.Server.Implementations.Session
return _trueTaskResult;
}
+
+ ///
+ /// Reports the playback start.
+ ///
+ /// The message.
+ private void ReportPlaybackStart(WebSocketMessageInfo message)
+ {
+ _logger.Debug("Received PlaybackStart message");
+
+ var session = _sessionManager.Sessions
+ .FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
+
+ if (session != null && session.User != null)
+ {
+ var vals = message.Data.Split('|');
+
+ var item = _dtoService.GetItemByDtoId(vals[0]);
+
+ var queueableMediaTypes = string.Empty;
+ var canSeek = true;
+
+ if (vals.Length > 1)
+ {
+ canSeek = string.Equals(vals[1], "true", StringComparison.OrdinalIgnoreCase);
+ }
+ if (vals.Length > 2)
+ {
+ queueableMediaTypes = vals[2];
+ }
+
+ var info = new PlaybackInfo
+ {
+ CanSeek = canSeek,
+ Item = item,
+ SessionId = session.Id,
+ QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
+ };
+
+ _sessionManager.OnPlaybackStart(info);
+ }
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
new file mode 100644
index 000000000..6915cfc64
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
@@ -0,0 +1,92 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Session;
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Session
+{
+ public class WebSocketController : ISessionRemoteController
+ {
+ public bool Supports(SessionInfo session)
+ {
+ return session.WebSockets.Any(i => i.State == WebSocketState.Open);
+ }
+
+ private IWebSocketConnection GetSocket(SessionInfo session)
+ {
+ var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
+
+
+ if (socket == null)
+ {
+ throw new InvalidOperationException("The requested session does not have an open web socket.");
+ }
+
+ return socket;
+ }
+
+ public Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage
+ {
+ MessageType = "SystemCommand",
+ Data = command.ToString()
+
+ }, cancellationToken);
+ }
+
+ public Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage
+ {
+ MessageType = "MessageCommand",
+ Data = command
+
+ }, cancellationToken);
+ }
+
+ public Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage
+ {
+ MessageType = "Play",
+ Data = command
+
+ }, cancellationToken);
+ }
+
+ public Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage
+ {
+ MessageType = "Browse",
+ Data = command
+
+ }, cancellationToken);
+ }
+
+ public Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage
+ {
+ MessageType = "Playstate",
+ Data = command
+
+ }, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/WebSocket/AlchemyWebSocket.cs b/MediaBrowser.Server.Implementations/WebSocket/AlchemyWebSocket.cs
index 958201625..de998254c 100644
--- a/MediaBrowser.Server.Implementations/WebSocket/AlchemyWebSocket.cs
+++ b/MediaBrowser.Server.Implementations/WebSocket/AlchemyWebSocket.cs
@@ -92,7 +92,9 @@ namespace MediaBrowser.Server.Implementations.WebSocket
/// Task.
public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken)
{
- return Task.Run(() => UserContext.Send(bytes));
+ UserContext.Send(bytes);
+
+ return Task.FromResult(true);
}
///
diff --git a/MediaBrowser.Server.Mono/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.Server.Mono/FFMpeg/FFMpegDownloader.cs
new file mode 100644
index 000000000..cc268ef07
--- /dev/null
+++ b/MediaBrowser.Server.Mono/FFMpeg/FFMpegDownloader.cs
@@ -0,0 +1,36 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.ServerApplication.FFMpeg
+{
+ public class FFMpegDownloader
+ {
+ private readonly IHttpClient _httpClient;
+ private readonly IApplicationPaths _appPaths;
+ private readonly ILogger _logger;
+ private readonly IZipClient _zipClient;
+
+ public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient)
+ {
+ _logger = logger;
+ _appPaths = appPaths;
+ _httpClient = httpClient;
+ _zipClient = zipClient;
+ }
+
+ public Task GetFFMpegInfo()
+ {
+ return Task.FromResult (new FFMpegInfo());
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/MainWindow.cs b/MediaBrowser.Server.Mono/MainWindow.cs
new file mode 100644
index 000000000..229f44dab
--- /dev/null
+++ b/MediaBrowser.Server.Mono/MainWindow.cs
@@ -0,0 +1,16 @@
+using System;
+using Gtk;
+
+public partial class MainWindow: Gtk.Window
+{
+ public MainWindow (): base (Gtk.WindowType.Toplevel)
+ {
+ Build ();
+ }
+
+ protected void OnDeleteEvent (object sender, DeleteEventArgs a)
+ {
+ Application.Quit ();
+ a.RetVal = true;
+ }
+}
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
new file mode 100644
index 000000000..1c369daac
--- /dev/null
+++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
@@ -0,0 +1,119 @@
+
+
+
+ Debug
+ x86
+ 10.0.0
+ 2.0
+ {A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}
+ WinExe
+ MediaBrowser.Server.Mono
+ MediaBrowser.Server.Mono
+ v4.5
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ x86
+ false
+
+
+ full
+ true
+ bin\Release
+ prompt
+ 4
+ x86
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ gui.stetic
+
+
+
+
+
+
+
+
+
+ EntryPoints\StartupWizard.cs
+
+
+ Native\BrowserLauncher.cs
+
+
+
+
+
+ FFMpeg\FFMpegInfo.cs
+
+
+ ApplicationHost.cs
+
+
+
+
+
+
+
+
+
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}
+ MediaBrowser.WebDashboard
+
+
+ {2E781478-814D-4A48-9D80-BFF206441A65}
+ MediaBrowser.Server.Implementations
+
+
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}
+ MediaBrowser.Providers
+
+
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
+ MediaBrowser.Model
+
+
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}
+ MediaBrowser.Controller
+
+
+ {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}
+ MediaBrowser.Common.Implementations
+
+
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
+ MediaBrowser.Common
+
+
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}
+ MediaBrowser.Api
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/Native/Assemblies.cs b/MediaBrowser.Server.Mono/Native/Assemblies.cs
new file mode 100644
index 000000000..eae6366e1
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/Assemblies.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Assemblies
+ ///
+ public static class Assemblies
+ {
+ ///
+ /// Gets the assemblies with parts.
+ ///
+ /// List{Assembly}.
+ public static List GetAssembliesWithParts()
+ {
+ var list = new List();
+
+ return list;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/Autorun.cs b/MediaBrowser.Server.Mono/Native/Autorun.cs
new file mode 100644
index 000000000..ee33c5967
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/Autorun.cs
@@ -0,0 +1,20 @@
+using System;
+using System.IO;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Autorun
+ ///
+ public static class Autorun
+ {
+ ///
+ /// Configures the specified autorun.
+ ///
+ /// if set to true [autorun].
+ public static void Configure(bool autorun)
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/HttpMessageHandlerFactory.cs b/MediaBrowser.Server.Mono/Native/HttpMessageHandlerFactory.cs
new file mode 100644
index 000000000..5823a7e51
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/HttpMessageHandlerFactory.cs
@@ -0,0 +1,25 @@
+using System.Net;
+using System.Net.Cache;
+using System.Net.Http;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class HttpMessageHandlerFactory
+ ///
+ public static class HttpMessageHandlerFactory
+ {
+ ///
+ /// Gets the HTTP message handler.
+ ///
+ /// if set to true [enable HTTP compression].
+ /// HttpMessageHandler.
+ public static HttpMessageHandler GetHttpMessageHandler(bool enableHttpCompression)
+ {
+ return new HttpClientHandler
+ {
+ AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/NativeApp.cs b/MediaBrowser.Server.Mono/Native/NativeApp.cs
new file mode 100644
index 000000000..bb47f6ea4
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/NativeApp.cs
@@ -0,0 +1,25 @@
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class NativeApp
+ ///
+ public static class NativeApp
+ {
+ ///
+ /// Shutdowns this instance.
+ ///
+ public static void Shutdown()
+ {
+
+ }
+
+ ///
+ /// Restarts this instance.
+ ///
+ public static void Restart()
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/ServerAuthorization.cs b/MediaBrowser.Server.Mono/Native/ServerAuthorization.cs
new file mode 100644
index 000000000..6f43a12c0
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/ServerAuthorization.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Authorization
+ ///
+ public static class ServerAuthorization
+ {
+ ///
+ /// Authorizes the server.
+ ///
+ /// The HTTP server port.
+ /// The HTTP server URL prefix.
+ /// The web socket port.
+ /// The UDP port.
+ /// The temp directory.
+ public static void AuthorizeServer(int httpServerPort, string httpServerUrlPrefix, int webSocketPort, int udpPort, string tempDirectory)
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/Sqlite.cs b/MediaBrowser.Server.Mono/Native/Sqlite.cs
new file mode 100644
index 000000000..cc20952d7
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/Sqlite.cs
@@ -0,0 +1,36 @@
+using System.Data;
+using System.Data.SQLite;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Sqlite
+ ///
+ public static class Sqlite
+ {
+ ///
+ /// Connects to db.
+ ///
+ /// The db path.
+ /// Task{IDbConnection}.
+ /// dbPath
+ public static async Task OpenDatabase(string dbPath)
+ {
+ var connectionstr = new SQLiteConnectionStringBuilder
+ {
+ PageSize = 4096,
+ CacheSize = 4096,
+ SyncMode = SynchronizationModes.Normal,
+ DataSource = dbPath,
+ JournalMode = SQLiteJournalModeEnum.Wal
+ };
+
+ var connection = new SQLiteConnection(connectionstr.ConnectionString);
+
+ await connection.OpenAsync().ConfigureAwait(false);
+
+ return connection;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
new file mode 100644
index 000000000..72dee1162
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -0,0 +1,16 @@
+using System;
+using Gtk;
+
+namespace MediaBrowser.Server.Mono
+{
+ class MainClass
+ {
+ public static void Main (string[] args)
+ {
+ Application.Init ();
+ MainWindow win = new MainWindow ();
+ win.Show ();
+ Application.Run ();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs b/MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0a2e93220
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: AssemblyTitle ("MediaBrowser.Server.Mono")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("Luke")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+[assembly: AssemblyVersion ("1.0.*")]
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/MediaBrowser.Server.Mono/gtk-gui/MainWindow.cs b/MediaBrowser.Server.Mono/gtk-gui/MainWindow.cs
new file mode 100644
index 000000000..c481dfc8c
--- /dev/null
+++ b/MediaBrowser.Server.Mono/gtk-gui/MainWindow.cs
@@ -0,0 +1,20 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+public partial class MainWindow
+{
+ protected virtual void Build ()
+ {
+ global::Stetic.Gui.Initialize (this);
+ // Widget MainWindow
+ this.Name = "MainWindow";
+ this.Title = global::Mono.Unix.Catalog.GetString ("MainWindow");
+ this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+ if ((this.Child != null)) {
+ this.Child.ShowAll ();
+ }
+ this.DefaultWidth = 400;
+ this.DefaultHeight = 300;
+ this.Show ();
+ this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent);
+ }
+}
diff --git a/MediaBrowser.Server.Mono/gtk-gui/generated.cs b/MediaBrowser.Server.Mono/gtk-gui/generated.cs
new file mode 100644
index 000000000..9ef336398
--- /dev/null
+++ b/MediaBrowser.Server.Mono/gtk-gui/generated.cs
@@ -0,0 +1,29 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace Stetic
+{
+ internal class Gui
+ {
+ private static bool initialized;
+
+ internal static void Initialize (Gtk.Widget iconRenderer)
+ {
+ if ((Stetic.Gui.initialized == false)) {
+ Stetic.Gui.initialized = true;
+ }
+ }
+ }
+
+ internal class ActionGroups
+ {
+ public static Gtk.ActionGroup GetActionGroup (System.Type type)
+ {
+ return Stetic.ActionGroups.GetActionGroup (type.FullName);
+ }
+
+ public static Gtk.ActionGroup GetActionGroup (string name)
+ {
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/gtk-gui/gui.stetic b/MediaBrowser.Server.Mono/gtk-gui/gui.stetic
new file mode 100644
index 000000000..81685442c
--- /dev/null
+++ b/MediaBrowser.Server.Mono/gtk-gui/gui.stetic
@@ -0,0 +1,20 @@
+
+
+
+ ..
+ 2.12
+
+
+
+
+
+
+
+ MainWindow
+ CenterOnParent
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs
index 69de391a4..706206d3a 100644
--- a/MediaBrowser.ServerApplication/App.xaml.cs
+++ b/MediaBrowser.ServerApplication/App.xaml.cs
@@ -154,58 +154,5 @@ namespace MediaBrowser.ServerApplication
{
Dispatcher.Invoke(Shutdown);
}
-
- ///
- /// Opens the dashboard page.
- ///
- /// The page.
- /// The logged in user.
- /// The configuration manager.
- /// The app host.
- public static void OpenDashboardPage(string page, User loggedInUser, IServerConfigurationManager configurationManager, IServerApplicationHost appHost)
- {
- var url = "http://localhost:" + configurationManager.Configuration.HttpServerPortNumber + "/" +
- appHost.WebApplicationName + "/dashboard/" + page;
-
- OpenUrl(url);
- }
-
- ///
- /// Opens the URL.
- ///
- /// The URL.
- public static void OpenUrl(string url)
- {
- var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = url
- },
-
- EnableRaisingEvents = true
- };
-
- process.Exited += ProcessExited;
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- MessageBox.Show("There was an error launching your web browser. Please check your defualt browser settings.");
- }
- }
-
- ///
- /// Processes the exited.
- ///
- /// The sender.
- /// The instance containing the event data.
- static void ProcessExited(object sender, EventArgs e)
- {
- ((Process)sender).Dispose();
- }
}
}
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 5cae99785..d0f7da73d 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -24,7 +24,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
-using MediaBrowser.IsoMounter;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
@@ -36,6 +35,7 @@ using MediaBrowser.Server.Implementations.BdInfo;
using MediaBrowser.Server.Implementations.Configuration;
using MediaBrowser.Server.Implementations.Drawing;
using MediaBrowser.Server.Implementations.Dto;
+using MediaBrowser.Server.Implementations.EntryPoints;
using MediaBrowser.Server.Implementations.HttpServer;
using MediaBrowser.Server.Implementations.IO;
using MediaBrowser.Server.Implementations.Library;
@@ -46,16 +46,14 @@ using MediaBrowser.Server.Implementations.Providers;
using MediaBrowser.Server.Implementations.ServerManager;
using MediaBrowser.Server.Implementations.Session;
using MediaBrowser.Server.Implementations.WebSocket;
-using MediaBrowser.ServerApplication.Implementations;
+using MediaBrowser.ServerApplication.FFMpeg;
+using MediaBrowser.ServerApplication.Native;
using MediaBrowser.WebDashboard.Api;
using System;
using System.Collections.Generic;
-using System.Data.SQLite;
-using System.Diagnostics;
+using System.Data;
using System.IO;
using System.Linq;
-using System.Net;
-using System.Net.Cache;
using System.Net.Http;
using System.Reflection;
using System.Threading;
@@ -68,8 +66,6 @@ namespace MediaBrowser.ServerApplication
///
public class ApplicationHost : BaseApplicationHost, IServerApplicationHost
{
- internal const int UdpServerPort = 7359;
-
///
/// Gets the server kernel.
///
@@ -142,11 +138,6 @@ namespace MediaBrowser.ServerApplication
/// The provider manager.
private IProviderManager ProviderManager { get; set; }
///
- /// Gets or sets the zip client.
- ///
- /// The zip client.
- private IZipClient ZipClient { get; set; }
- ///
/// Gets or sets the HTTP server.
///
/// The HTTP server.
@@ -161,6 +152,7 @@ namespace MediaBrowser.ServerApplication
private IMediaEncoder MediaEncoder { get; set; }
private IIsoManager IsoManager { get; set; }
+ private ISessionManager SessionManager { get; set; }
private ILocalizationManager LocalizationManager { get; set; }
@@ -174,14 +166,6 @@ namespace MediaBrowser.ServerApplication
private IItemRepository ItemRepository { get; set; }
private INotificationsRepository NotificationsRepository { get; set; }
- ///
- /// The full path to our startmenu shortcut
- ///
- protected override string ProductShortcutPath
- {
- get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk"); }
- }
-
private Task _httpServerCreationTask;
///
@@ -255,8 +239,7 @@ namespace MediaBrowser.ServerApplication
RegisterSingleInstance(() => new BdInfoExaminer());
- ZipClient = new DotNetZipClient();
- RegisterSingleInstance(ZipClient);
+ var mediaEncoderTask = RegisterMediaEncoder();
UserDataRepository = new SqliteUserDataRepository(ApplicationPaths, JsonSerializer, LogManager);
RegisterSingleInstance(UserDataRepository);
@@ -284,10 +267,8 @@ namespace MediaBrowser.ServerApplication
RegisterSingleInstance(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager));
- await RegisterMediaEncoder().ConfigureAwait(false);
-
- var clientConnectionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository);
- RegisterSingleInstance(clientConnectionManager);
+ SessionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository);
+ RegisterSingleInstance(SessionManager);
HttpServer = await _httpServerCreationTask.ConfigureAwait(false);
RegisterSingleInstance(HttpServer, false);
@@ -310,7 +291,7 @@ namespace MediaBrowser.ServerApplication
await ConfigureNotificationsRepository().ConfigureAwait(false);
- await Task.WhenAll(itemsTask, displayPreferencesTask, userdataTask).ConfigureAwait(false);
+ await Task.WhenAll(itemsTask, displayPreferencesTask, userdataTask, mediaEncoderTask).ConfigureAwait(false);
SetKernelProperties();
}
@@ -406,27 +387,14 @@ namespace MediaBrowser.ServerApplication
/// The db path.
/// Task{IDbConnection}.
/// dbPath
- private static async Task ConnectToDb(string dbPath)
+ private static Task ConnectToDb(string dbPath)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException("dbPath");
}
- var connectionstr = new SQLiteConnectionStringBuilder
- {
- PageSize = 4096,
- CacheSize = 4096,
- SyncMode = SynchronizationModes.Normal,
- DataSource = dbPath,
- JournalMode = SQLiteJournalModeEnum.Wal
- };
-
- var connection = new SQLiteConnection(connectionstr.ConnectionString);
-
- await connection.OpenAsync().ConfigureAwait(false);
-
- return connection;
+ return Sqlite.OpenDatabase(dbPath);
}
///
@@ -477,6 +445,8 @@ namespace MediaBrowser.ServerApplication
IsoManager.AddParts(GetExports());
+ SessionManager.AddParts(GetExports());
+
ImageProcessor.AddParts(GetExports());
}
@@ -527,7 +497,6 @@ namespace MediaBrowser.ServerApplication
{
NotifyPendingRestart();
}
-
}
///
@@ -544,7 +513,7 @@ namespace MediaBrowser.ServerApplication
Logger.ErrorException("Error sending server restart web socket message", ex);
}
- MainStartup.Restart();
+ NativeApp.Restart();
}
///
@@ -568,44 +537,44 @@ namespace MediaBrowser.ServerApplication
/// IEnumerable{Assembly}.
protected override IEnumerable GetComposablePartAssemblies()
{
+ var list = Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
+ .Select(LoadAssembly)
+ .Where(a => a != null)
+ .ToList();
+
// Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
// This will prevent the .dll file from getting locked, and allow us to replace it when needed
- foreach (var pluginAssembly in Directory
- .EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
- .Select(LoadAssembly).Where(a => a != null))
- {
- yield return pluginAssembly;
- }
// Include composable parts in the Api assembly
- yield return typeof(ApiEntryPoint).Assembly;
+ list.Add(typeof(ApiEntryPoint).Assembly);
// Include composable parts in the Dashboard assembly
- yield return typeof(DashboardInfo).Assembly;
+ list.Add(typeof(DashboardInfo).Assembly);
// Include composable parts in the Model assembly
- yield return typeof(SystemInfo).Assembly;
+ list.Add(typeof(SystemInfo).Assembly);
// Include composable parts in the Common assembly
- yield return typeof(IApplicationHost).Assembly;
+ list.Add(typeof(IApplicationHost).Assembly);
// Include composable parts in the Controller assembly
- yield return typeof(Kernel).Assembly;
+ list.Add(typeof(Kernel).Assembly);
// Include composable parts in the Providers assembly
- yield return typeof(ImagesByNameProvider).Assembly;
+ list.Add(typeof(ImagesByNameProvider).Assembly);
// Common implementations
- yield return typeof(TaskManager).Assembly;
+ list.Add(typeof(TaskManager).Assembly);
// Server implementations
- yield return typeof(ServerApplicationPaths).Assembly;
+ list.Add(typeof(ServerApplicationPaths).Assembly);
- // Pismo
- yield return typeof(PismoIsoManager).Assembly;
+ list.AddRange(Assemblies.GetAssembliesWithParts());
// Include composable parts in the running assembly
- yield return GetType().Assembly;
+ list.Add(GetType().Assembly);
+
+ return list;
}
private readonly string _systemId = Environment.MachineName.GetMD5().ToString();
@@ -664,7 +633,7 @@ namespace MediaBrowser.ServerApplication
Logger.ErrorException("Error sending server shutdown web socket message", ex);
}
- MainStartup.Shutdown();
+ NativeApp.Shutdown();
}
///
@@ -674,36 +643,16 @@ namespace MediaBrowser.ServerApplication
{
Logger.Info("Requesting administrative access to authorize http server");
- // Create a temp file path to extract the bat file to
- var tmpFile = Path.Combine(ConfigurationManager.CommonApplicationPaths.TempDirectory, Guid.NewGuid() + ".bat");
-
- // Extract the bat file
- using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.ServerApplication.RegisterServer.bat"))
+ try
{
- using (var fileStream = File.Create(tmpFile))
- {
- stream.CopyTo(fileStream);
- }
+ ServerAuthorization.AuthorizeServer(ServerConfigurationManager.Configuration.HttpServerPortNumber,
+ HttpServerUrlPrefix, ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber,
+ UdpServerEntryPoint.PortNumber,
+ ConfigurationManager.CommonApplicationPaths.TempDirectory);
}
-
- var startInfo = new ProcessStartInfo
+ catch (Exception ex)
{
- FileName = tmpFile,
-
- Arguments = string.Format("{0} {1} {2} {3}", ServerConfigurationManager.Configuration.HttpServerPortNumber,
- HttpServerUrlPrefix,
- UdpServerPort,
- ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber),
-
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- Verb = "runas",
- ErrorDialog = false
- };
-
- using (var process = Process.Start(startInfo))
- {
- process.WaitForExit();
+ Logger.ErrorException("Error authorizing server", ex);
}
}
@@ -713,8 +662,7 @@ namespace MediaBrowser.ServerApplication
/// The cancellation token.
/// The progress.
/// Task{CheckForUpdateResult}.
- public override async Task CheckForApplicationUpdate(CancellationToken cancellationToken,
- IProgress progress)
+ public override async Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress)
{
var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
@@ -745,11 +693,12 @@ namespace MediaBrowser.ServerApplication
/// HttpMessageHandler.
protected override HttpMessageHandler GetHttpMessageHandler(bool enableHttpCompression)
{
- return new WebRequestHandler
- {
- CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate),
- AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None
- };
+ return HttpMessageHandlerFactory.GetHttpMessageHandler(enableHttpCompression);
+ }
+
+ protected override void ConfigureAutoRunAtStartup(bool autorun)
+ {
+ Autorun.Configure(autorun);
}
}
}
diff --git a/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs b/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
index 87578ef84..1a5f9e2c3 100644
--- a/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
+++ b/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
@@ -3,9 +3,10 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
-using System.ComponentModel;
+using System;
using System.Linq;
-using System.Windows;
+using System.Windows.Forms;
+using MediaBrowser.ServerApplication.Native;
namespace MediaBrowser.ServerApplication.EntryPoints
{
@@ -31,9 +32,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints
///
/// The app host.
/// The user manager.
- public StartupWizard(IServerApplicationHost appHost, IUserManager userManager, IServerConfigurationManager configurationManager)
+ public StartupWizard(IServerApplicationHost appHost, IUserManager userManager, IServerConfigurationManager configurationManager, ILogger logger)
{
_appHost = appHost;
+ _logger = logger;
_userManager = userManager;
_configurationManager = configurationManager;
}
@@ -58,9 +60,9 @@ namespace MediaBrowser.ServerApplication.EntryPoints
try
{
- App.OpenDashboardPage("wizardstart.html", user, _configurationManager, _appHost);
+ BrowserLauncher.OpenDashboardPage("wizardstart.html", user, _configurationManager, _appHost, _logger);
}
- catch (Win32Exception ex)
+ catch (Exception ex)
{
_logger.ErrorException("Error launching startup wizard", ex);
@@ -75,4 +77,4 @@ namespace MediaBrowser.ServerApplication.EntryPoints
{
}
}
-}
+}
\ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
new file mode 100644
index 000000000..926767f5b
--- /dev/null
+++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
@@ -0,0 +1,302 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.ServerApplication.FFMpeg
+{
+ public class FFMpegDownloader
+ {
+ private readonly IHttpClient _httpClient;
+ private readonly IApplicationPaths _appPaths;
+ private readonly ILogger _logger;
+ private readonly IZipClient _zipClient;
+
+ private const string Version = "ffmpeg20130904";
+
+ private readonly string[] _fontUrls = new[]
+ {
+ "https://www.dropbox.com/s/pj847twf7riq0j7/ARIALUNI.7z?dl=1"
+ };
+
+ private readonly string[] _ffMpegUrls = new[]
+ {
+ "https://github.com/MediaBrowser/MediaBrowser/raw/master/MediaBrowser.ServerApplication/FFMpeg/ffmpeg-20130904-git-f974289-win32-static.7z",
+
+ "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20130904-git-f974289-win32-static.7z",
+ "https://www.dropbox.com/s/a81cb2ob23fwcfs/ffmpeg-20130904-git-f974289-win32-static.7z?dl=1"
+ };
+
+ public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient)
+ {
+ _logger = logger;
+ _appPaths = appPaths;
+ _httpClient = httpClient;
+ _zipClient = zipClient;
+ }
+
+ public async Task GetFFMpegInfo()
+ {
+ var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true), Version);
+
+ var info = new FFMpegInfo
+ {
+ ProbePath = Path.Combine(versionedDirectoryPath, "ffprobe.exe"),
+ Path = Path.Combine(versionedDirectoryPath, "ffmpeg.exe"),
+ Version = Version
+ };
+
+ if (!Directory.Exists(versionedDirectoryPath))
+ {
+ Directory.CreateDirectory(versionedDirectoryPath);
+ }
+
+ var tasks = new List();
+
+ if (!File.Exists(info.ProbePath) || !File.Exists(info.Path))
+ {
+ tasks.Add(DownloadFFMpeg(info));
+ }
+
+ tasks.Add(DownloadFonts(versionedDirectoryPath));
+
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+
+ return info;
+ }
+
+ private async Task DownloadFFMpeg(FFMpegInfo info)
+ {
+ foreach (var url in _ffMpegUrls)
+ {
+ try
+ {
+ var tempFile = await DownloadFFMpeg(info, url).ConfigureAwait(false);
+
+ ExtractFFMpeg(tempFile, Path.GetDirectoryName(info.Path));
+ return;
+ }
+ catch (HttpException ex)
+ {
+
+ }
+ }
+
+ throw new ApplicationException("Unable to download required components. Please try again later.");
+ }
+
+ private Task DownloadFFMpeg(FFMpegInfo info, string url)
+ {
+ return _httpClient.GetTempFile(new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = CancellationToken.None,
+ Progress = new Progress(),
+
+ // Make it look like a browser
+ // Try to hide that we're direct linking
+ UserAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.47 Safari/537.36"
+ });
+ }
+
+ private void ExtractFFMpeg(string tempFile, string targetFolder)
+ {
+ _logger.Debug("Extracting ffmpeg from {0}", tempFile);
+
+ var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString());
+
+ if (!Directory.Exists(tempFolder))
+ {
+ Directory.CreateDirectory(tempFolder);
+ }
+
+ try
+ {
+ Extract7zArchive(tempFile, tempFolder);
+
+ var files = Directory.EnumerateFiles(tempFolder, "*.exe", SearchOption.AllDirectories).ToList();
+
+ foreach (var file in files)
+ {
+ File.Copy(file, Path.Combine(targetFolder, Path.GetFileName(file)));
+ }
+ }
+ finally
+ {
+ DeleteFile(tempFile);
+ }
+ }
+
+ private void Extract7zArchive(string archivePath, string targetPath)
+ {
+ _zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
+ }
+
+ private void DeleteFile(string path)
+ {
+ try
+ {
+ File.Delete(path);
+ }
+ catch (IOException ex)
+ {
+ _logger.ErrorException("Error deleting temp file {0}", ex, path);
+ }
+ }
+
+ ///
+ /// Extracts the fonts.
+ ///
+ /// The target path.
+ private async Task DownloadFonts(string targetPath)
+ {
+ try
+ {
+ var fontsDirectory = Path.Combine(targetPath, "fonts");
+
+ if (!Directory.Exists(fontsDirectory))
+ {
+ Directory.CreateDirectory(fontsDirectory);
+ }
+
+ const string fontFilename = "ARIALUNI.TTF";
+
+ var fontFile = Path.Combine(fontsDirectory, fontFilename);
+
+ if (!File.Exists(fontFile))
+ {
+ await DownloadFontFile(fontsDirectory, fontFilename).ConfigureAwait(false);
+ }
+
+ await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
+ }
+ catch (HttpException ex)
+ {
+ // Don't let the server crash because of this
+ _logger.ErrorException("Error downloading ffmpeg font files", ex);
+ }
+ catch (Exception ex)
+ {
+ // Don't let the server crash because of this
+ _logger.ErrorException("Error writing ffmpeg font files", ex);
+ }
+ }
+
+ ///
+ /// Downloads the font file.
+ ///
+ /// The fonts directory.
+ /// The font filename.
+ /// Task.
+ private async Task DownloadFontFile(string fontsDirectory, string fontFilename)
+ {
+ var existingFile = Directory
+ .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
+ .FirstOrDefault();
+
+ if (existingFile != null)
+ {
+ try
+ {
+ File.Copy(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
+ return;
+ }
+ catch (IOException ex)
+ {
+ // Log this, but don't let it fail the operation
+ _logger.ErrorException("Error copying file", ex);
+ }
+ }
+
+ string tempFile = null;
+
+ foreach (var url in _fontUrls)
+ {
+ try
+ {
+ tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
+ {
+ Url = url,
+ Progress = new Progress()
+
+ }).ConfigureAwait(false);
+
+ break;
+ }
+ catch (Exception ex)
+ {
+ // The core can function without the font file, so handle this
+ _logger.ErrorException("Failed to download ffmpeg font file from {0}", ex, url);
+ }
+ }
+
+ if (string.IsNullOrEmpty(tempFile))
+ {
+ return;
+ }
+
+ Extract7zArchive(tempFile, fontsDirectory);
+
+ try
+ {
+ File.Delete(tempFile);
+ }
+ catch (IOException ex)
+ {
+ // Log this, but don't let it fail the operation
+ _logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
+ }
+ }
+
+ ///
+ /// Writes the font config file.
+ ///
+ /// The fonts directory.
+ /// Task.
+ private async Task WriteFontConfigFile(string fontsDirectory)
+ {
+ const string fontConfigFilename = "fonts.conf";
+ var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
+
+ if (!File.Exists(fontConfigFile))
+ {
+ var contents = string.Format("{0}ArialArial Unicode MS", fontsDirectory);
+
+ var bytes = Encoding.UTF8.GetBytes(contents);
+
+ using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
+ FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize,
+ FileOptions.Asynchronous))
+ {
+ await fileStream.WriteAsync(bytes, 0, bytes.Length);
+ }
+ }
+ }
+
+ ///
+ /// Gets the media tools path.
+ ///
+ /// if set to true [create].
+ /// System.String.
+ private string GetMediaToolsPath(bool create)
+ {
+ var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
+
+ if (create && !Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ return path;
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs
new file mode 100644
index 000000000..147a9f771
--- /dev/null
+++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs
@@ -0,0 +1,24 @@
+namespace MediaBrowser.ServerApplication.FFMpeg
+{
+ ///
+ /// Class FFMpegInfo
+ ///
+ public class FFMpegInfo
+ {
+ ///
+ /// Gets or sets the path.
+ ///
+ /// The path.
+ public string Path { get; set; }
+ ///
+ /// Gets or sets the probe path.
+ ///
+ /// The probe path.
+ public string ProbePath { get; set; }
+ ///
+ /// Gets or sets the version.
+ ///
+ /// The version.
+ public string Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/FFMpeg/ffmpeg-20130904-git-f974289-win32-static.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/FFMpeg/ffmpeg-20130904-git-f974289-win32-static.7z.REMOVED.git-id
new file mode 100644
index 000000000..9f83b949b
--- /dev/null
+++ b/MediaBrowser.ServerApplication/FFMpeg/ffmpeg-20130904-git-f974289-win32-static.7z.REMOVED.git-id
@@ -0,0 +1 @@
+8f1dfd62d31e48c31bef4b9ccc0e514f46650a79
\ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Implementations/DotNetZipClient.cs b/MediaBrowser.ServerApplication/Implementations/DotNetZipClient.cs
deleted file mode 100644
index 3b174a9b2..000000000
--- a/MediaBrowser.ServerApplication/Implementations/DotNetZipClient.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Ionic.Zip;
-using MediaBrowser.Model.IO;
-using System.IO;
-
-namespace MediaBrowser.ServerApplication.Implementations
-{
- ///
- /// Class DotNetZipClient
- ///
- public class DotNetZipClient : IZipClient
- {
- ///
- /// Extracts all.
- ///
- /// The source file.
- /// The target path.
- /// if set to true [overwrite existing files].
- public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
- {
- using (var fileStream = File.OpenRead(sourceFile))
- {
- using (var zipFile = ZipFile.Read(fileStream))
- {
- zipFile.ExtractAll(targetPath, overwriteExistingFiles ? ExtractExistingFileAction.OverwriteSilently : ExtractExistingFileAction.DoNotOverwrite);
- }
- }
- }
-
- ///
- /// Extracts all.
- ///
- /// The source.
- /// The target path.
- /// if set to true [overwrite existing files].
- public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles)
- {
- using (var zipFile = ZipFile.Read(source))
- {
- zipFile.ExtractAll(targetPath, overwriteExistingFiles ? ExtractExistingFileAction.OverwriteSilently : ExtractExistingFileAction.DoNotOverwrite);
- }
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs
deleted file mode 100644
index 7fd0acddd..000000000
--- a/MediaBrowser.ServerApplication/Implementations/FFMpegDownloader.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.ServerApplication.Implementations
-{
- public class FFMpegDownloader
- {
- private readonly IZipClient _zipClient;
- private readonly IHttpClient _httpClient;
- private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
-
- public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient)
- {
- _logger = logger;
- _appPaths = appPaths;
- _httpClient = httpClient;
- _zipClient = zipClient;
- }
-
- public async Task GetFFMpegInfo()
- {
- var assembly = GetType().Assembly;
-
- var prefix = GetType().Namespace + ".";
-
- var srch = prefix + "ffmpeg";
-
- var resource = assembly.GetManifestResourceNames().First(r => r.StartsWith(srch));
-
- var filename =
- resource.Substring(resource.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) + prefix.Length);
-
- var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true),
- Path.GetFileNameWithoutExtension(filename));
-
- if (!Directory.Exists(versionedDirectoryPath))
- {
- Directory.CreateDirectory(versionedDirectoryPath);
- }
-
- await ExtractTools(assembly, resource, versionedDirectoryPath).ConfigureAwait(false);
-
- return new FFMpegInfo
- {
- ProbePath = Path.Combine(versionedDirectoryPath, "ffprobe.exe"),
- Path = Path.Combine(versionedDirectoryPath, "ffmpeg.exe"),
- Version = Path.GetFileNameWithoutExtension(versionedDirectoryPath)
- };
- }
-
- ///
- /// Extracts the tools.
- ///
- /// The assembly.
- /// The zip file resource path.
- /// The target path.
- private async Task ExtractTools(Assembly assembly, string zipFileResourcePath, string targetPath)
- {
- using (var resourceStream = assembly.GetManifestResourceStream(zipFileResourcePath))
- {
- _zipClient.ExtractAll(resourceStream, targetPath, false);
- }
-
- try
- {
- await DownloadFonts(targetPath).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting ffmpeg font files", ex);
- }
- }
-
- private const string FontUrl = "https://www.dropbox.com/s/9nb76tybcsw5xrk/ARIALUNI.zip?dl=1";
-
- ///
- /// Extracts the fonts.
- ///
- /// The target path.
- private async Task DownloadFonts(string targetPath)
- {
- var fontsDirectory = Path.Combine(targetPath, "fonts");
-
- if (!Directory.Exists(fontsDirectory))
- {
- Directory.CreateDirectory(fontsDirectory);
- }
-
- const string fontFilename = "ARIALUNI.TTF";
-
- var fontFile = Path.Combine(fontsDirectory, fontFilename);
-
- if (!File.Exists(fontFile))
- {
- await DownloadFontFile(fontsDirectory, fontFilename).ConfigureAwait(false);
- }
-
- await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
- }
-
- ///
- /// Downloads the font file.
- ///
- /// The fonts directory.
- /// The font filename.
- /// Task.
- private async Task DownloadFontFile(string fontsDirectory, string fontFilename)
- {
- var existingFile = Directory
- .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
- .FirstOrDefault();
-
- if (existingFile != null)
- {
- try
- {
- File.Copy(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
- return;
- }
- catch (IOException ex)
- {
- // Log this, but don't let it fail the operation
- _logger.ErrorException("Error copying file", ex);
- }
- }
-
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = FontUrl,
- Progress = new Progress()
- });
-
- _zipClient.ExtractAll(tempFile, fontsDirectory, true);
-
- try
- {
- File.Delete(tempFile);
- }
- catch (IOException ex)
- {
- // Log this, but don't let it fail the operation
- _logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
- }
- }
-
- ///
- /// Writes the font config file.
- ///
- /// The fonts directory.
- /// Task.
- private async Task WriteFontConfigFile(string fontsDirectory)
- {
- const string fontConfigFilename = "fonts.conf";
- var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
-
- if (!File.Exists(fontConfigFile))
- {
- var contents = string.Format("{0}ArialArial Unicode MS", fontsDirectory);
-
- var bytes = Encoding.UTF8.GetBytes(contents);
-
- using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
- FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize,
- FileOptions.Asynchronous))
- {
- await fileStream.WriteAsync(bytes, 0, bytes.Length);
- }
- }
- }
-
- ///
- /// Gets the media tools path.
- ///
- /// if set to true [create].
- /// System.String.
- private string GetMediaToolsPath(bool create)
- {
- var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
-
- if (create && !Directory.Exists(path))
- {
- Directory.CreateDirectory(path);
- }
-
- return path;
- }
- }
-
- public class FFMpegInfo
- {
- public string Path { get; set; }
- public string ProbePath { get; set; }
- public string Version { get; set; }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Implementations/ffmpeg20130904.zip.REMOVED.git-id b/MediaBrowser.ServerApplication/Implementations/ffmpeg20130904.zip.REMOVED.git-id
deleted file mode 100644
index e99d115a4..000000000
--- a/MediaBrowser.ServerApplication/Implementations/ffmpeg20130904.zip.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-3496b2cde22e7c4cb56b480dd2da637167d51e78
\ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Implementations/readme.txt b/MediaBrowser.ServerApplication/Implementations/readme.txt
deleted file mode 100644
index b32dd9aec..000000000
--- a/MediaBrowser.ServerApplication/Implementations/readme.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is the 32-bit static build of ffmpeg, located at:
-
-http://ffmpeg.zeranoe.com/builds/
-
-The zip file contains both ffmpeg and ffprobe, and is suffixed with the date of the build.
\ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
index e9c1fdc99..55fa60ed2 100644
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -11,7 +11,6 @@ using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Threading;
-using System.Threading.Tasks;
using System.Windows;
namespace MediaBrowser.ServerApplication
diff --git a/MediaBrowser.ServerApplication/MainWindow.xaml.cs b/MediaBrowser.ServerApplication/MainWindow.xaml.cs
index 4c9c065e6..c22c35be8 100644
--- a/MediaBrowser.ServerApplication/MainWindow.xaml.cs
+++ b/MediaBrowser.ServerApplication/MainWindow.xaml.cs
@@ -12,6 +12,7 @@ using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
+using MediaBrowser.ServerApplication.Native;
namespace MediaBrowser.ServerApplication
{
@@ -188,19 +189,19 @@ namespace MediaBrowser.ServerApplication
/// The instance containing the event data.
void cmdApiDocs_Click(object sender, EventArgs e)
{
- App.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
- _appHost.WebApplicationName + "/metadata");
+ BrowserLauncher.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
+ _appHost.WebApplicationName + "/metadata", _logger);
}
void cmdSwaggerApiDocs_Click(object sender, EventArgs e)
{
- App.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
- _appHost.WebApplicationName + "/swagger-ui/index.html");
+ BrowserLauncher.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
+ _appHost.WebApplicationName + "/swagger-ui/index.html", _logger);
}
void cmdGithubWiki_Click(object sender, EventArgs e)
{
- App.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki");
+ BrowserLauncher.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki", _logger);
}
///
@@ -254,7 +255,7 @@ namespace MediaBrowser.ServerApplication
///
private void OpenDashboard(User loggedInUser)
{
- App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost);
+ BrowserLauncher.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost, _logger);
}
///
@@ -264,7 +265,7 @@ namespace MediaBrowser.ServerApplication
/// The instance containing the event data.
private void cmVisitCT_click(object sender, RoutedEventArgs e)
{
- App.OpenUrl("http://community.mediabrowser.tv/");
+ BrowserLauncher.OpenUrl("http://community.mediabrowser.tv/", _logger);
}
///
@@ -275,7 +276,7 @@ namespace MediaBrowser.ServerApplication
private void cmdBrowseLibrary_click(object sender, RoutedEventArgs e)
{
var user = _userManager.Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
- App.OpenDashboardPage("index.html", user, _configurationManager, _appHost);
+ BrowserLauncher.OpenDashboardPage("index.html", user, _configurationManager, _appHost, _logger);
}
///
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 043d5c18f..61ec19dd5 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -130,10 +130,6 @@
False
..\packages\MediaBrowser.IsoMounting.3.0.56\lib\net45\MediaBrowser.IsoMounter.dll
-
- False
- ..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll
-
False
..\packages\NLog.2.0.1.2\lib\net45\NLog.dll
@@ -187,7 +183,6 @@
-
@@ -209,12 +204,19 @@
Component
-
-
+
+
+
+
+
+
+
+
Component
+
SplashWindow.xaml
@@ -242,7 +244,6 @@
Code
-
LibraryExplorer.xaml
@@ -278,14 +279,15 @@
Resources.Designer.cs
-
+
+
SettingsSingleFileGenerator
Settings.Designer.cs
-
+
@@ -390,9 +392,6 @@
-
-
-
if $(ConfigurationName) == Release (
diff --git a/MediaBrowser.ServerApplication/Native/Assemblies.cs b/MediaBrowser.ServerApplication/Native/Assemblies.cs
new file mode 100644
index 000000000..b43dc1a10
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/Assemblies.cs
@@ -0,0 +1,25 @@
+using MediaBrowser.IsoMounter;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Assemblies
+ ///
+ public static class Assemblies
+ {
+ ///
+ /// Gets the assemblies with parts.
+ ///
+ /// List{Assembly}.
+ public static List GetAssembliesWithParts()
+ {
+ var list = new List();
+
+ list.Add(typeof(PismoIsoManager).Assembly);
+
+ return list;
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Native/Autorun.cs b/MediaBrowser.ServerApplication/Native/Autorun.cs
new file mode 100644
index 000000000..d1c02db84
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/Autorun.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Autorun
+ ///
+ public static class Autorun
+ {
+ ///
+ /// Configures the specified autorun.
+ ///
+ /// if set to true [autorun].
+ public static void Configure(bool autorun)
+ {
+ var shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk");
+
+ if (autorun)
+ {
+ //Copy our shortut into the startup folder for this user
+ File.Copy(shortcutPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"), true);
+ }
+ else
+ {
+ //Remove our shortcut from the startup folder for this user
+ File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"));
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Native/BrowserLauncher.cs b/MediaBrowser.ServerApplication/Native/BrowserLauncher.cs
new file mode 100644
index 000000000..e7d041d15
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/BrowserLauncher.cs
@@ -0,0 +1,68 @@
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ public static class BrowserLauncher
+ {
+ ///
+ /// Opens the dashboard page.
+ ///
+ /// The page.
+ /// The logged in user.
+ /// The configuration manager.
+ /// The app host.
+ public static void OpenDashboardPage(string page, User loggedInUser, IServerConfigurationManager configurationManager, IServerApplicationHost appHost, ILogger logger)
+ {
+ var url = "http://localhost:" + configurationManager.Configuration.HttpServerPortNumber + "/" +
+ appHost.WebApplicationName + "/dashboard/" + page;
+
+ OpenUrl(url, logger);
+ }
+
+ ///
+ /// Opens the URL.
+ ///
+ /// The URL.
+ public static void OpenUrl(string url, ILogger logger)
+ {
+ var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = url
+ },
+
+ EnableRaisingEvents = true
+ };
+
+ process.Exited += ProcessExited;
+
+ try
+ {
+ process.Start();
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error launching url: {0}", ex, url);
+
+ MessageBox.Show("There was an error launching your web browser. Please check your default browser settings.");
+ }
+ }
+
+ ///
+ /// Processes the exited.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void ProcessExited(object sender, EventArgs e)
+ {
+ ((Process)sender).Dispose();
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Native/HttpMessageHandlerFactory.cs b/MediaBrowser.ServerApplication/Native/HttpMessageHandlerFactory.cs
new file mode 100644
index 000000000..4bbcc9ea0
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/HttpMessageHandlerFactory.cs
@@ -0,0 +1,26 @@
+using System.Net;
+using System.Net.Cache;
+using System.Net.Http;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class HttpMessageHandlerFactory
+ ///
+ public static class HttpMessageHandlerFactory
+ {
+ ///
+ /// Gets the HTTP message handler.
+ ///
+ /// if set to true [enable HTTP compression].
+ /// HttpMessageHandler.
+ public static HttpMessageHandler GetHttpMessageHandler(bool enableHttpCompression)
+ {
+ return new WebRequestHandler
+ {
+ CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate),
+ AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Native/NativeApp.cs b/MediaBrowser.ServerApplication/Native/NativeApp.cs
new file mode 100644
index 000000000..ea4218afc
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/NativeApp.cs
@@ -0,0 +1,25 @@
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class NativeApp
+ ///
+ public static class NativeApp
+ {
+ ///
+ /// Shutdowns this instance.
+ ///
+ public static void Shutdown()
+ {
+ MainStartup.Shutdown();
+ }
+
+ ///
+ /// Restarts this instance.
+ ///
+ public static void Restart()
+ {
+ MainStartup.Restart();
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/RegisterServer.bat b/MediaBrowser.ServerApplication/Native/RegisterServer.bat
similarity index 100%
rename from MediaBrowser.ServerApplication/RegisterServer.bat
rename to MediaBrowser.ServerApplication/Native/RegisterServer.bat
diff --git a/MediaBrowser.ServerApplication/Native/ServerAuthorization.cs b/MediaBrowser.ServerApplication/Native/ServerAuthorization.cs
new file mode 100644
index 000000000..91f0974eb
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/ServerAuthorization.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Authorization
+ ///
+ public static class ServerAuthorization
+ {
+ ///
+ /// Authorizes the server.
+ ///
+ /// The HTTP server port.
+ /// The HTTP server URL prefix.
+ /// The web socket port.
+ /// The UDP port.
+ /// The temp directory.
+ public static void AuthorizeServer(int httpServerPort, string httpServerUrlPrefix, int webSocketPort, int udpPort, string tempDirectory)
+ {
+ // Create a temp file path to extract the bat file to
+ var tmpFile = Path.Combine(tempDirectory, Guid.NewGuid() + ".bat");
+
+ // Extract the bat file
+ using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(typeof(ServerAuthorization).Namespace + ".RegisterServer.bat"))
+ {
+ using (var fileStream = File.Create(tmpFile))
+ {
+ stream.CopyTo(fileStream);
+ }
+ }
+
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = tmpFile,
+
+ Arguments = string.Format("{0} {1} {2} {3}", httpServerPort,
+ httpServerUrlPrefix,
+ udpPort,
+ webSocketPort),
+
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ Verb = "runas",
+ ErrorDialog = false
+ };
+
+ using (var process = Process.Start(startInfo))
+ {
+ process.WaitForExit();
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Native/Sqlite.cs b/MediaBrowser.ServerApplication/Native/Sqlite.cs
new file mode 100644
index 000000000..cc20952d7
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/Sqlite.cs
@@ -0,0 +1,36 @@
+using System.Data;
+using System.Data.SQLite;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+ ///
+ /// Class Sqlite
+ ///
+ public static class Sqlite
+ {
+ ///
+ /// Connects to db.
+ ///
+ /// The db path.
+ /// Task{IDbConnection}.
+ /// dbPath
+ public static async Task OpenDatabase(string dbPath)
+ {
+ var connectionstr = new SQLiteConnectionStringBuilder
+ {
+ PageSize = 4096,
+ CacheSize = 4096,
+ SyncMode = SynchronizationModes.Normal,
+ DataSource = dbPath,
+ JournalMode = SQLiteJournalModeEnum.Wal
+ };
+
+ var connection = new SQLiteConnection(connectionstr.ConnectionString);
+
+ await connection.OpenAsync().ConfigureAwait(false);
+
+ return connection;
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png b/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png
index fbc769a6f..12db84679 100644
Binary files a/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png and b/MediaBrowser.ServerApplication/Resources/Images/mb3logo800.png differ
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
index 8c1821ca5..e680b556f 100644
--- a/MediaBrowser.ServerApplication/packages.config
+++ b/MediaBrowser.ServerApplication/packages.config
@@ -4,7 +4,6 @@
-
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index d139adfc3..189812a3c 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -3200,7 +3200,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
* @param {String} userId
* @param {String} itemId
*/
- self.reportPlaybackStart = function (userId, itemId) {
+ self.reportPlaybackStart = function (userId, itemId, canSeek, queueableMediaTypes) {
if (!userId) {
throw new Error("null userId");
@@ -3210,17 +3210,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
throw new Error("null itemId");
}
+ canSeek = canSeek || false;
+ queueableMediaTypes = queueableMediaTypes || '';
+
if (self.isWebSocketOpen()) {
var deferred = $.Deferred();
- self.sendWebSocketMessage("PlaybackStart", itemId);
+ var msg = [itemId, canSeek, queueableMediaTypes];
+
+ self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
deferred.resolveWith(null, []);
return deferred.promise();
}
- var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId);
+ var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
+
+ CanSeek: canSeek,
+ QueueableMediaTypes: queueableMediaTypes
+ });
return self.ajax({
type: "POST",
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 06f930238..6a599da45 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -13,6 +13,8 @@
512
..\
true
+ 10.0.0
+ 2.0
true
@@ -35,24 +37,21 @@
Always
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
-
-
- False
- ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
-
-
- False
- ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
-
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll
+
+
+ ..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll
+
+
+ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll
+
@@ -67,15 +66,15 @@
- {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}
MediaBrowser.Common
- {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}
MediaBrowser.Controller
- {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}
MediaBrowser.Model
@@ -255,10 +254,10 @@
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 25b4f7b47..1c5a0f818 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index 1d104c45b..eb846cd2f 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Common.Internal
- 3.0.206
+ 3.0.208
MediaBrowser.Common.Internal
Luke
ebr,Luke,scottisafool
@@ -12,7 +12,7 @@
Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.
Copyright © Media Browser 2013
-
+
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 0670bcacf..ba211c3d6 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Common
- 3.0.206
+ 3.0.208
MediaBrowser.Common
Media Browser Team
ebr,Luke,scottisafool
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index f5aa2d460..c6f801a9b 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Server.Core
- 3.0.206
+ 3.0.208
Media Browser.Server.Core
Media Browser Team
ebr,Luke,scottisafool
@@ -12,7 +12,7 @@
Contains core components required to build plugins for Media Browser Server.
Copyright © Media Browser 2013
-
+