mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
support multiple remote control outputs
This commit is contained in:
parent
c61cc4a304
commit
f176307e59
@ -62,7 +62,7 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
var jobCount = _activeTranscodingJobs.Count;
|
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
|
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
|
||||||
if (jobCount > 0)
|
if (jobCount > 0)
|
||||||
@ -84,7 +84,8 @@ namespace MediaBrowser.Api
|
|||||||
/// <param name="process">The process.</param>
|
/// <param name="process">The process.</param>
|
||||||
/// <param name="isVideo">if set to <c>true</c> [is video].</param>
|
/// <param name="isVideo">if set to <c>true</c> [is video].</param>
|
||||||
/// <param name="startTimeTicks">The start time ticks.</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)
|
lock (_activeTranscodingJobs)
|
||||||
{
|
{
|
||||||
@ -95,7 +96,8 @@ namespace MediaBrowser.Api
|
|||||||
Process = process,
|
Process = process,
|
||||||
ActiveRequestCount = 1,
|
ActiveRequestCount = 1,
|
||||||
IsVideo = isVideo,
|
IsVideo = isVideo,
|
||||||
StartTimeTicks = startTimeTicks
|
StartTimeTicks = startTimeTicks,
|
||||||
|
SourcePath = sourcePath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,10 +198,47 @@ namespace MediaBrowser.Api
|
|||||||
/// Called when [transcode kill timer stopped].
|
/// Called when [transcode kill timer stopped].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
private async void OnTranscodeKillTimerStopped(object state)
|
private void OnTranscodeKillTimerStopped(object state)
|
||||||
{
|
{
|
||||||
var job = (TranscodingJob)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)
|
lock (_activeTranscodingJobs)
|
||||||
{
|
{
|
||||||
_activeTranscodingJobs.Remove(job);
|
_activeTranscodingJobs.Remove(job);
|
||||||
@ -373,6 +412,7 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
public bool IsVideo { get; set; }
|
public bool IsVideo { get; set; }
|
||||||
public long? StartTimeTicks { get; set; }
|
public long? StartTimeTicks { get; set; }
|
||||||
|
public string SourcePath { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
181
MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs
Normal file
181
MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs
Normal file
@ -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.
|
||||||
|
/// <0 Executed before global request filters
|
||||||
|
/// >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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,7 @@
|
|||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Session;
|
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using ServiceStack.Common.Web;
|
|
||||||
using ServiceStack.ServiceHost;
|
using ServiceStack.ServiceHost;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -15,7 +13,7 @@ namespace MediaBrowser.Api
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class BaseApiService
|
/// Class BaseApiService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequestFilter]
|
[AuthorizationRequestFilter]
|
||||||
public class BaseApiService : IHasResultFactory, IRestfulService
|
public class BaseApiService : IHasResultFactory, IRestfulService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -308,147 +306,4 @@ namespace MediaBrowser.Api
|
|||||||
return item;
|
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.
|
|
||||||
/// <0 Executed before global request filters
|
|
||||||
/// >0 Executed after global request filters
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The priority.</value>
|
|
||||||
public int Priority
|
|
||||||
{
|
|
||||||
get { return 0; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
<Compile Include="DefaultTheme\Models.cs" />
|
<Compile Include="DefaultTheme\Models.cs" />
|
||||||
<Compile Include="DisplayPreferencesService.cs" />
|
<Compile Include="DisplayPreferencesService.cs" />
|
||||||
<Compile Include="EnvironmentService.cs" />
|
<Compile Include="EnvironmentService.cs" />
|
||||||
|
<Compile Include="AuthorizationRequestFilterAttribute.cs" />
|
||||||
<Compile Include="GamesService.cs" />
|
<Compile Include="GamesService.cs" />
|
||||||
<Compile Include="Images\ImageByNameService.cs" />
|
<Compile Include="Images\ImageByNameService.cs" />
|
||||||
<Compile Include="Images\ImageRequest.cs" />
|
<Compile Include="Images\ImageRequest.cs" />
|
||||||
@ -88,6 +89,8 @@
|
|||||||
<Compile Include="PackageService.cs" />
|
<Compile Include="PackageService.cs" />
|
||||||
<Compile Include="Playback\Hls\AudioHlsService.cs" />
|
<Compile Include="Playback\Hls\AudioHlsService.cs" />
|
||||||
<Compile Include="Playback\Hls\BaseHlsService.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\Hls\VideoHlsService.cs" />
|
||||||
<Compile Include="Playback\Progressive\AudioService.cs" />
|
<Compile Include="Playback\Progressive\AudioService.cs" />
|
||||||
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
|
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
|
||||||
@ -143,7 +146,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
<ItemGroup>
|
||||||
|
<Folder Include="Filters\" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
|
@ -613,7 +613,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
EnableRaisingEvents = true
|
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);
|
Logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using MediaBrowser.Model.Dto;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using ServiceStack.ServiceHost;
|
using ServiceStack.ServiceHost;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Hls
|
namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
@ -20,27 +19,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>
|
/// <summary>
|
||||||
/// Class AudioHlsService
|
/// Class AudioHlsService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -59,20 +37,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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(ApplicationPaths.EncodedMediaCachePath, file);
|
|
||||||
|
|
||||||
return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the specified request.
|
/// Gets the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -10,7 +10,6 @@ using MediaBrowser.Model.IO;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -213,29 +212,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
return count;
|
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>
|
/// <summary>
|
||||||
/// Gets the command line arguments.
|
/// Gets the command line arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
53
MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs
Normal file
53
MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
Normal file
147
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
Normal file
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,6 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using ServiceStack.ServiceHost;
|
using ServiceStack.ServiceHost;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Hls
|
namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
@ -31,44 +30,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>
|
/// <summary>
|
||||||
/// Class VideoHlsService
|
/// Class VideoHlsService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -82,38 +43,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="libraryManager">The library manager.</param>
|
||||||
/// <param name="isoManager">The iso manager.</param>
|
/// <param name="isoManager">The iso manager.</param>
|
||||||
/// <param name="mediaEncoder">The media encoder.</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)
|
public VideoHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService)
|
||||||
: base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService)
|
: base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the specified request.
|
|
||||||
/// </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>
|
/// <summary>
|
||||||
/// Gets the specified request.
|
/// Gets the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Dto;
|
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Net;
|
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using ServiceStack.ServiceHost;
|
using ServiceStack.ServiceHost;
|
||||||
using System;
|
using System;
|
||||||
@ -189,6 +187,7 @@ namespace MediaBrowser.Api
|
|||||||
/// Initializes a new instance of the <see cref="SessionsService" /> class.
|
/// Initializes a new instance of the <see cref="SessionsService" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sessionManager">The session manager.</param>
|
/// <param name="sessionManager">The session manager.</param>
|
||||||
|
/// <param name="dtoService">The dto service.</param>
|
||||||
public SessionsService(ISessionManager sessionManager, IDtoService dtoService)
|
public SessionsService(ISessionManager sessionManager, IDtoService dtoService)
|
||||||
{
|
{
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
@ -214,52 +213,15 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
public void Post(SendPlaystateCommand request)
|
public void Post(SendPlaystateCommand request)
|
||||||
{
|
{
|
||||||
var task = SendPlaystateCommand(request);
|
var command = new PlaystateRequest
|
||||||
|
|
||||||
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<PlaystateRequest>
|
|
||||||
{
|
|
||||||
MessageType = "Playstate",
|
|
||||||
|
|
||||||
Data = new PlaystateRequest
|
|
||||||
{
|
{
|
||||||
Command = request.Command,
|
Command = request.Command,
|
||||||
SeekPositionTicks = request.SeekPositionTicks
|
SeekPositionTicks = request.SeekPositionTicks
|
||||||
}
|
};
|
||||||
|
|
||||||
}, CancellationToken.None).ConfigureAwait(false);
|
var task = _sessionManager.SendPlaystateCommand(request.Id, command, CancellationToken.None);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
Task.WaitAll(task);
|
||||||
{
|
|
||||||
Logger.ErrorException("Error sending web socket message", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The requested session does not have an open web socket.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -268,57 +230,19 @@ namespace MediaBrowser.Api
|
|||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
public void Post(BrowseTo 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);
|
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)
|
|
||||||
{
|
|
||||||
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<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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Posts the specified request.
|
/// Posts the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -336,53 +260,16 @@ namespace MediaBrowser.Api
|
|||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
public void Post(SendMessageCommand request)
|
public void Post(SendMessageCommand request)
|
||||||
{
|
{
|
||||||
var task = SendMessageCommand(request);
|
var command = new MessageCommand
|
||||||
|
|
||||||
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<MessageCommand>
|
|
||||||
{
|
|
||||||
MessageType = "MessageCommand",
|
|
||||||
|
|
||||||
Data = new MessageCommand
|
|
||||||
{
|
{
|
||||||
Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
|
Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
|
||||||
TimeoutMs = request.TimeoutMs,
|
TimeoutMs = request.TimeoutMs,
|
||||||
Text = request.Text
|
Text = request.Text
|
||||||
}
|
};
|
||||||
|
|
||||||
}, CancellationToken.None).ConfigureAwait(false);
|
var task = _sessionManager.SendMessageCommand(request.Id, command, CancellationToken.None);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
Task.WaitAll(task);
|
||||||
{
|
|
||||||
Logger.ErrorException("Error sending web socket message", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The requested session does not have an open web socket.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -391,62 +278,17 @@ namespace MediaBrowser.Api
|
|||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
public void Post(Play request)
|
public void Post(Play request)
|
||||||
{
|
{
|
||||||
var task = Play(request);
|
var command = new PlayRequest
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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<PlayRequest>
|
|
||||||
{
|
|
||||||
MessageType = "Play",
|
|
||||||
|
|
||||||
Data = new PlayRequest
|
|
||||||
{
|
{
|
||||||
ItemIds = request.ItemIds.Split(',').ToArray(),
|
ItemIds = request.ItemIds.Split(',').ToArray(),
|
||||||
|
|
||||||
PlayCommand = request.PlayCommand,
|
PlayCommand = request.PlayCommand,
|
||||||
StartPositionTicks = request.StartPositionTicks
|
StartPositionTicks = request.StartPositionTicks
|
||||||
}
|
};
|
||||||
|
|
||||||
}, CancellationToken.None).ConfigureAwait(false);
|
var task = _sessionManager.SendPlayCommand(request.Id, command, CancellationToken.None);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
Task.WaitAll(task);
|
||||||
{
|
|
||||||
Logger.ErrorException("Error sending web socket message", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The requested session does not have an open web socket.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -663,19 +663,11 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
|
|
||||||
private SessionInfo GetSession()
|
private SessionInfo GetSession()
|
||||||
{
|
{
|
||||||
var auth = RequestFilterAttribute.GetAuthorization(RequestContext);
|
var auth = AuthorizationRequestFilterAttribute.GetAuthorization(RequestContext);
|
||||||
|
|
||||||
string deviceId;
|
return _sessionManager.Sessions.First(i => string.Equals(i.DeviceId, auth.DeviceId) &&
|
||||||
string client;
|
string.Equals(i.Client, auth.Client) &&
|
||||||
string version;
|
string.Equals(i.ApplicationVersion, auth.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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -726,7 +718,12 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
|
|
||||||
var item = _dtoService.GetItemByDtoId(request.Id, user.Id);
|
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);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
|
@ -89,5 +89,41 @@ namespace MediaBrowser.Controller.Session
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SendSystemCommand(Guid sessionId, SystemCommand command, CancellationToken cancellationToken);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,5 +21,41 @@ namespace MediaBrowser.Controller.Session
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,5 +465,69 @@ namespace MediaBrowser.Server.Implementations.Session
|
|||||||
|
|
||||||
return Task.WhenAll(tasks);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using System;
|
using System;
|
||||||
@ -11,42 +11,82 @@ namespace MediaBrowser.Server.Implementations.Session
|
|||||||
{
|
{
|
||||||
public class WebSocketController : ISessionRemoteController
|
public class WebSocketController : ISessionRemoteController
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
public WebSocketController(ILogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Supports(SessionInfo session)
|
public bool Supports(SessionInfo session)
|
||||||
{
|
{
|
||||||
return session.WebSockets.Any(i => i.State == WebSocketState.Open);
|
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);
|
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
|
||||||
|
|
||||||
if (socket != null)
|
|
||||||
|
if (socket == null)
|
||||||
{
|
{
|
||||||
try
|
throw new InvalidOperationException("The requested session does not have an open web socket.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await socket.SendAsync(new WebSocketMessage<string>
|
var socket = GetSocket(session);
|
||||||
|
|
||||||
|
return socket.SendAsync(new WebSocketMessage<string>
|
||||||
{
|
{
|
||||||
MessageType = "SystemCommand",
|
MessageType = "SystemCommand",
|
||||||
Data = command.ToString()
|
Data = command.ToString()
|
||||||
|
|
||||||
}, cancellationToken).ConfigureAwait(false);
|
}, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
public Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error sending web socket message", ex);
|
var socket = GetSocket(session);
|
||||||
}
|
|
||||||
}
|
return socket.SendAsync(new WebSocketMessage<MessageCommand>
|
||||||
else
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The requested session does not have an open web socket.");
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user