aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs48
-rw-r--r--MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs181
-rw-r--r--MediaBrowser.Api/BaseApiService.cs147
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj7
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs2
-rw-r--r--MediaBrowser.Api/Playback/Hls/AudioHlsService.cs36
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs24
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs53
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs147
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs67
-rw-r--r--MediaBrowser.Api/SessionsService.cs218
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs23
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs36
-rw-r--r--MediaBrowser.Controller/Session/ISessionRemoteController.cs36
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs64
-rw-r--r--MediaBrowser.Server.Implementations/Session/WebSocketController.cs94
16 files changed, 677 insertions, 506 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 52707c3c6..273d9a7a9 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
/// <param name="process">The process.</param>
/// <param name="isVideo">if set to <c>true</c> [is video].</param>
/// <param name="startTimeTicks">The start time ticks.</param>
- public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, bool isVideo, long? startTimeTicks)
+ /// <param name="sourcePath">The source path.</param>
+ 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
});
}
}
@@ -196,10 +198,47 @@ namespace MediaBrowser.Api
/// Called when [transcode kill timer stopped].
/// </summary>
/// <param name="state">The state.</param>
- private async void OnTranscodeKillTimerStopped(object state)
+ private void OnTranscodeKillTimerStopped(object state)
{
var job = (TranscodingJob)state;
+ KillTranscodingJob(job);
+ }
+
+ /// <summary>
+ /// Kills the single transcoding job.
+ /// </summary>
+ /// <param name="sourcePath">The source path.</param>
+ internal void KillSingleTranscodingJob(string sourcePath)
+ {
+ if (string.IsNullOrEmpty(sourcePath))
+ {
+ throw new ArgumentNullException("sourcePath");
+ }
+
+ var jobs = new List<TranscodingJob>();
+
+ 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());
+ }
+ }
+
+ /// <summary>
+ /// Kills the transcoding job.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ 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; }
}
/// <summary>
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
+ /// <summary>
+ /// Gets or sets the user manager.
+ /// </summary>
+ /// <value>The user manager.</value>
+ public IUserManager UserManager { get; set; }
+
+ public ISessionManager SessionManager { get; set; }
+
+ /// <summary>
+ /// Gets or sets the logger.
+ /// </summary>
+ /// <value>The logger.</value>
+ public ILogger Logger { get; set; }
+
+ /// <summary>
+ /// The request filter is executed before the service.
+ /// </summary>
+ /// <param name="request">The http request wrapper</param>
+ /// <param name="response">The http response wrapper</param>
+ /// <param name="requestDto">The request DTO</param>
+ 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);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the auth.
+ /// </summary>
+ /// <param name="httpReq">The HTTP req.</param>
+ /// <returns>Dictionary{System.StringSystem.String}.</returns>
+ public static Dictionary<string, string> GetAuthorization(IHttpRequest httpReq)
+ {
+ var auth = httpReq.Headers[HttpHeaders.Authorization];
+
+ return GetAuthorization(auth);
+ }
+
+ /// <summary>
+ /// Gets the authorization.
+ /// </summary>
+ /// <param name="httpReq">The HTTP req.</param>
+ /// <returns>Dictionary{System.StringSystem.String}.</returns>
+ 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
+ };
+ }
+
+ /// <summary>
+ /// Gets the authorization.
+ /// </summary>
+ /// <param name="authorizationHeader">The authorization header.</param>
+ /// <returns>Dictionary{System.StringSystem.String}.</returns>
+ private static Dictionary<string, string> 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<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var item in parts)
+ {
+ var param = item.Trim().Split(new[] { '=' }, 2);
+ result.Add(param[0], param[1].Trim(new[] { '"' }));
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// A new shallow copy of this filter is used on every request.
+ /// </summary>
+ /// <returns>IHasRequestFilter.</returns>
+ public IHasRequestFilter Copy()
+ {
+ return this;
+ }
+
+ /// <summary>
+ /// Order in which Request Filters are executed.
+ /// &lt;0 Executed before global request filters
+ /// &gt;0 Executed after global request filters
+ /// </summary>
+ /// <value>The priority.</value>
+ 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
/// <summary>
/// Class BaseApiService
/// </summary>
- [RequestFilter]
+ [AuthorizationRequestFilter]
public class BaseApiService : IHasResultFactory, IRestfulService
{
/// <summary>
@@ -308,147 +306,4 @@ namespace MediaBrowser.Api
return item;
}
}
-
- /// <summary>
- /// Class RequestFilterAttribute
- /// </summary>
- public class RequestFilterAttribute : Attribute, IHasRequestFilter
- {
- //This property will be resolved by the IoC container
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
-
- public ISessionManager SessionManager { get; set; }
-
- /// <summary>
- /// Gets or sets the logger.
- /// </summary>
- /// <value>The logger.</value>
- public ILogger Logger { get; set; }
-
- /// <summary>
- /// The request filter is executed before the service.
- /// </summary>
- /// <param name="request">The http request wrapper</param>
- /// <param name="response">The http response wrapper</param>
- /// <param name="requestDto">The request DTO</param>
- 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);
- }
- }
- }
-
- /// <summary>
- /// Gets the auth.
- /// </summary>
- /// <param name="httpReq">The HTTP req.</param>
- /// <returns>Dictionary{System.StringSystem.String}.</returns>
- public static Dictionary<string, string> GetAuthorization(IHttpRequest httpReq)
- {
- var auth = httpReq.Headers[HttpHeaders.Authorization];
-
- return GetAuthorization(auth);
- }
-
- /// <summary>
- /// Gets the authorization.
- /// </summary>
- /// <param name="httpReq">The HTTP req.</param>
- /// <returns>Dictionary{System.StringSystem.String}.</returns>
- public static Dictionary<string, string> GetAuthorization(IRequestContext httpReq)
- {
- var auth = httpReq.GetHeader("Authorization");
-
- return GetAuthorization(auth);
- }
-
- /// <summary>
- /// Gets the authorization.
- /// </summary>
- /// <param name="authorizationHeader">The authorization header.</param>
- /// <returns>Dictionary{System.StringSystem.String}.</returns>
- private static Dictionary<string, string> 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<string, string>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var item in parts)
- {
- var param = item.Trim().Split(new[] { '=' }, 2);
- result.Add(param[0], param[1].Trim(new[] { '"' }));
- }
-
- return result;
- }
-
- /// <summary>
- /// A new shallow copy of this filter is used on every request.
- /// </summary>
- /// <returns>IHasRequestFilter.</returns>
- public IHasRequestFilter Copy()
- {
- return this;
- }
-
- /// <summary>
- /// Order in which Request Filters are executed.
- /// &lt;0 Executed before global request filters
- /// &gt;0 Executed after global request filters
- /// </summary>
- /// <value>The priority.</value>
- public int Priority
- {
- get { return 0; }
- }
- }
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index c7cca812f..995b5cdf1 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -70,6 +70,7 @@
<Compile Include="DefaultTheme\Models.cs" />
<Compile Include="DisplayPreferencesService.cs" />
<Compile Include="EnvironmentService.cs" />
+ <Compile Include="AuthorizationRequestFilterAttribute.cs" />
<Compile Include="GamesService.cs" />
<Compile Include="Images\ImageByNameService.cs" />
<Compile Include="Images\ImageRequest.cs" />
@@ -88,6 +89,8 @@
<Compile Include="PackageService.cs" />
<Compile Include="Playback\Hls\AudioHlsService.cs" />
<Compile Include="Playback\Hls\BaseHlsService.cs" />
+ <Compile Include="Playback\Hls\HlsSegmentResponseFilter.cs" />
+ <Compile Include="Playback\Hls\HlsSegmentService.cs" />
<Compile Include="Playback\Hls\VideoHlsService.cs" />
<Compile Include="Playback\Progressive\AudioService.cs" />
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
@@ -143,7 +146,9 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
- <ItemGroup />
+ <ItemGroup>
+ <Folder Include="Filters\" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
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
{
@@ -21,27 +20,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
- /// Class GetHlsAudioSegment
- /// </summary>
- [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
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the segment id.
- /// </summary>
- /// <value>The segment id.</value>
- public string SegmentId { get; set; }
- }
-
- /// <summary>
/// Class AudioHlsService
/// </summary>
public class AudioHlsService : BaseHlsService
@@ -64,20 +42,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- 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);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
public object Get(GetHlsAudioStream request)
{
return ProcessRequest(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);
- });
- }
- }
-
/// <summary>
/// Gets the command line arguments.
/// </summary>
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<string>(1);
+ var playlistId = pathInfo.GetArgumentValue<string>(3);
+
+ OnEndRequest(itemId, playlistId);
+ }
+
+ public IHasResponseFilter Copy()
+ {
+ return this;
+ }
+
+ public int Priority
+ {
+ get { return -1; }
+ }
+
+ /// <summary>
+ /// Called when [end request].
+ /// </summary>
+ /// <param name="itemId">The item id.</param>
+ /// <param name="playlistId">The playlist id.</param>
+ 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
+{
+ /// <summary>
+ /// Class GetHlsAudioSegment
+ /// </summary>
+ [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
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ public string Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the segment id.
+ /// </summary>
+ /// <value>The segment id.</value>
+ public string SegmentId { get; set; }
+ }
+
+ /// <summary>
+ /// Class GetHlsVideoSegment
+ /// </summary>
+ [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
+ [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
+ public class GetHlsVideoSegment
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ public string Id { get; set; }
+
+ public string PlaylistId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the segment id.
+ /// </summary>
+ /// <value>The segment id.</value>
+ public string SegmentId { get; set; }
+ }
+
+ /// <summary>
+ /// Class GetHlsVideoSegment
+ /// </summary>
+ [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
+ [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
+ public class GetHlsPlaylist
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ 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);
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ 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);
+ }
+
+ /// <summary>
+ /// Gets the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>System.Object.</returns>
+ 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);
+ }
+
+ /// <summary>
+ /// Called when [begin request].
+ /// </summary>
+ /// <param name="playlistId">The playlist id.</param>
+ 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
{
@@ -32,44 +31,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
- /// Class GetHlsVideoSegment
- /// </summary>
- [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
- public class GetHlsVideoSegment
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- public string PlaylistId { get; set; }
-
- /// <summary>
- /// Gets or sets the segment id.
- /// </summary>
- /// <value>The segment id.</value>
- public string SegmentId { get; set; }
- }
-
- /// <summary>
- /// Class GetHlsVideoSegment
- /// </summary>
- [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
- public class GetHlsPlaylist
- {
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- public string Id { get; set; }
-
- public string PlaylistId { get; set; }
- }
-
- /// <summary>
/// Class VideoHlsService
/// </summary>
public class VideoHlsService : BaseHlsService
@@ -82,6 +43,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <param name="libraryManager">The library manager.</param>
/// <param name="isoManager">The iso manager.</param>
/// <param name="mediaEncoder">The media encoder.</param>
+ /// <param name="dtoService">The dto service.</param>
public VideoHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService)
: base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService)
{
@@ -92,33 +54,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- 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);
- }
-
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
public object Get(GetHlsVideoStream request)
{
return ProcessRequest(request);
diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs
index b93b5326e..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 <see cref="SessionsService" /> class.
/// </summary>
/// <param name="sessionManager">The session manager.</param>
+ /// <param name="dtoService">The dto service.</param>
public SessionsService(ISessionManager sessionManager, IDtoService dtoService)
{
_sessionManager = sessionManager;
@@ -214,52 +213,15 @@ namespace MediaBrowser.Api
public void Post(SendPlaystateCommand request)
{
- var task = SendPlaystateCommand(request);
-
- Task.WaitAll(task);
- }
-
- private async Task SendPlaystateCommand(SendPlaystateCommand request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
+ var command = new PlaystateRequest
{
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
+ Command = request.Command,
+ SeekPositionTicks = request.SeekPositionTicks
+ };
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
+ var task = _sessionManager.SendPlaystateCommand(request.Id, command, CancellationToken.None);
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
-
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage<PlaystateRequest>
- {
- 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.");
- }
+ Task.WaitAll(task);
}
/// <summary>
@@ -268,55 +230,17 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public void Post(BrowseTo request)
{
- var task = BrowseTo(request);
-
- Task.WaitAll(task);
- }
-
- /// <summary>
- /// Browses to.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ResourceNotFoundException"></exception>
- /// <exception cref="System.ArgumentException"></exception>
- /// <exception cref="System.InvalidOperationException">The requested session does not have an open web socket.</exception>
- 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)
+ var command = new BrowseRequest
{
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
+ Context = request.Context,
+ ItemId = request.ItemId,
+ ItemName = request.ItemName,
+ ItemType = request.ItemType
+ };
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
+ var task = _sessionManager.SendBrowseCommand(request.Id, command, CancellationToken.None);
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage<BrowseTo>
- {
- 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.");
- }
+ Task.WaitAll(task);
}
/// <summary>
@@ -336,53 +260,16 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public void Post(SendMessageCommand request)
{
- var task = SendMessageCommand(request);
-
- 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)
+ var command = new MessageCommand
{
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
+ Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
+ TimeoutMs = request.TimeoutMs,
+ Text = request.Text
+ };
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
+ var task = _sessionManager.SendMessageCommand(request.Id, command, CancellationToken.None);
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage<MessageCommand>
- {
- 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.");
- }
+ Task.WaitAll(task);
}
/// <summary>
@@ -391,62 +278,17 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param>
public void Post(Play request)
{
- var task = Play(request);
-
- Task.WaitAll(task);
- }
-
- /// <summary>
- /// Plays the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task.</returns>
- /// <exception cref="ResourceNotFoundException"></exception>
- /// <exception cref="System.ArgumentException"></exception>
- /// <exception cref="System.InvalidOperationException">The requested session does not have an open web socket.</exception>
- private async Task Play(Play request)
- {
- var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
-
- if (session == null)
+ var command = new PlayRequest
{
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
- }
+ ItemIds = request.ItemIds.Split(',').ToArray(),
- if (!session.SupportsRemoteControl)
- {
- throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
- }
+ PlayCommand = request.PlayCommand,
+ StartPositionTicks = request.StartPositionTicks
+ };
- var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
+ var task = _sessionManager.SendPlayCommand(request.Id, command, CancellationToken.None);
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage<PlayRequest>
- {
- 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.");
- }
+ Task.WaitAll(task);
}
}
}
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 9085a3ecf..abd42910f 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -663,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));
}
/// <summary>
@@ -726,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.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index f8f7ded2b..0932ee52d 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -89,5 +89,41 @@ namespace MediaBrowser.Controller.Session
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendSystemCommand(Guid sessionId, SystemCommand command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the message command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendMessageCommand(Guid sessionId, MessageCommand command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the play command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendPlayCommand(Guid sessionId, PlayRequest command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the browse command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendBrowseCommand(Guid sessionId, BrowseRequest command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the playstate command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ 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
index 1f6faeb9c..9ba5c983d 100644
--- a/MediaBrowser.Controller/Session/ISessionRemoteController.cs
+++ b/MediaBrowser.Controller/Session/ISessionRemoteController.cs
@@ -21,5 +21,41 @@ namespace MediaBrowser.Controller.Session
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the message command.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the play command.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the browse command.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends the playstate command.
+ /// </summary>
+ /// <param name="session">The session.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index 5b0d957ae..79dfbc8a5 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -465,5 +465,69 @@ namespace MediaBrowser.Server.Implementations.Session
return Task.WhenAll(tasks);
}
+
+ /// <summary>
+ /// Sends the message command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ 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);
+ }
+
+ /// <summary>
+ /// Sends the play command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ 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);
+ }
+
+ /// <summary>
+ /// Sends the browse command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ 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);
+ }
+
+ /// <summary>
+ /// Sends the playstate command.
+ /// </summary>
+ /// <param name="sessionId">The session id.</param>
+ /// <param name="command">The command.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ 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/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
index daa4c7d81..6915cfc64 100644
--- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
+++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
@@ -1,5 +1,5 @@
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Logging;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using System;
@@ -11,42 +11,82 @@ namespace MediaBrowser.Server.Implementations.Session
{
public class WebSocketController : ISessionRemoteController
{
- private readonly ILogger _logger;
-
- public WebSocketController(ILogger logger)
- {
- _logger = logger;
- }
-
public bool Supports(SessionInfo session)
{
return session.WebSockets.Any(i => i.State == WebSocketState.Open);
}
- public async Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken)
+ private IWebSocketConnection GetSocket(SessionInfo session)
{
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
- if (socket != null)
- {
- try
- {
- await socket.SendAsync(new WebSocketMessage<string>
- {
- MessageType = "SystemCommand",
- Data = command.ToString()
-
- }, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending web socket message", ex);
- }
- }
- else
+
+ 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<string>
+ {
+ MessageType = "SystemCommand",
+ Data = command.ToString()
+
+ }, cancellationToken);
+ }
+
+ public Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage<MessageCommand>
+ {
+ MessageType = "MessageCommand",
+ Data = command
+
+ }, cancellationToken);
+ }
+
+ public Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage<PlayRequest>
+ {
+ MessageType = "Play",
+ Data = command
+
+ }, cancellationToken);
+ }
+
+ public Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage<BrowseRequest>
+ {
+ MessageType = "Browse",
+ Data = command
+
+ }, cancellationToken);
+ }
+
+ public Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken)
+ {
+ var socket = GetSocket(session);
+
+ return socket.SendAsync(new WebSocketMessage<PlaystateRequest>
+ {
+ MessageType = "Playstate",
+ Data = command
+
+ }, cancellationToken);
}
}
}