diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index ef415ec57c..281f764b5f 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -132,6 +132,7 @@ namespace MediaBrowser.Api
/// Called when [transcode beginning].
///
/// The path.
+ /// The stream identifier.
/// The transcoding job identifier.
/// The type.
/// The process.
@@ -140,6 +141,7 @@ namespace MediaBrowser.Api
/// The cancellation token source.
/// TranscodingJob.
public TranscodingJob OnTranscodeBeginning(string path,
+ string streamId,
string transcodingJobId,
TranscodingJobType type,
Process process,
@@ -157,7 +159,8 @@ namespace MediaBrowser.Api
ActiveRequestCount = 1,
DeviceId = deviceId,
CancellationTokenSource = cancellationTokenSource,
- Id = transcodingJobId
+ Id = transcodingJobId,
+ StreamId = streamId
};
_activeTranscodingJobs.Add(job);
@@ -316,17 +319,26 @@ namespace MediaBrowser.Api
/// Kills the single transcoding job.
///
/// The device id.
+ /// The stream identifier.
/// The delete files.
/// Task.
- /// deviceId
- internal void KillTranscodingJobs(string deviceId, Func deleteFiles)
+ internal void KillTranscodingJobs(string deviceId, string streamId, Func deleteFiles)
{
if (string.IsNullOrEmpty(deviceId))
{
throw new ArgumentNullException("deviceId");
}
- KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles);
+ KillTranscodingJobs(j =>
+ {
+ if (string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase))
+ {
+ return string.IsNullOrWhiteSpace(streamId) || string.Equals(streamId, j.StreamId, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return false;
+
+ }, deleteFiles);
}
///
@@ -335,7 +347,7 @@ namespace MediaBrowser.Api
/// The kill job.
/// The delete files.
/// Task.
- internal void KillTranscodingJobs(Func killJob, Func deleteFiles)
+ private void KillTranscodingJobs(Func killJob, Func deleteFiles)
{
var jobs = new List();
@@ -516,6 +528,11 @@ namespace MediaBrowser.Api
///
public class TranscodingJob
{
+ ///
+ /// Gets or sets the stream identifier.
+ ///
+ /// The stream identifier.
+ public string StreamId { get; set; }
///
/// Gets or sets the path.
///
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index dff433c9dc..4465be97a2 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Dto;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -72,6 +73,29 @@ namespace MediaBrowser.Api
return ResultFactory.GetOptimizedResultUsingCache(Request, cacheKey, lastDateModified, cacheDuration, factoryFn);
}
+ protected void AssertCanUpdateUser(IUserManager userManager, string userId)
+ {
+ var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+
+ var authenticatedUser = userManager.GetUserById(auth.UserId);
+
+ // If they're going to update the record of another user, they must be an administrator
+ if (!string.Equals(userId, auth.UserId, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!authenticatedUser.Policy.IsAdministrator)
+ {
+ throw new SecurityException("Unauthorized access.");
+ }
+ }
+ else
+ {
+ if (!authenticatedUser.Policy.EnableUserPreferenceAccess)
+ {
+ throw new SecurityException("Unauthorized access.");
+ }
+ }
+ }
+
///
/// To the optimized serialized result using cache.
///
@@ -88,9 +112,9 @@ namespace MediaBrowser.Api
/// Gets the session.
///
/// SessionInfo.
- protected SessionInfo GetSession()
+ protected async Task GetSession()
{
- var session = SessionContext.GetSession(Request);
+ var session = await SessionContext.GetSession(Request).ConfigureAwait(false);
if (session == null)
{
diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs
index 4bcd33d9e3..bdd2eeaadd 100644
--- a/MediaBrowser.Api/ConnectService.cs
+++ b/MediaBrowser.Api/ConnectService.cs
@@ -1,8 +1,10 @@
-using MediaBrowser.Common.Extensions;
+using System;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Connect;
+using MediaBrowser.Model.Dto;
using ServiceStack;
using System.Collections.Generic;
using System.Linq;
@@ -73,6 +75,28 @@ namespace MediaBrowser.Api
public string ConnectUserId { get; set; }
}
+ [Route("/Connect/Supporters", "GET")]
+ [Authenticated(Roles = "Admin")]
+ public class GetConnectSupporterSummary : IReturn
+ {
+ }
+
+ [Route("/Connect/Supporters", "DELETE")]
+ [Authenticated(Roles = "Admin")]
+ public class RemoveConnectSupporter : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Connect/Supporters", "POST")]
+ [Authenticated(Roles = "Admin")]
+ public class AddConnectSupporter : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string Id { get; set; }
+ }
+
public class ConnectService : BaseApiService
{
private readonly IConnectManager _connectManager;
@@ -84,6 +108,35 @@ namespace MediaBrowser.Api
_userManager = userManager;
}
+ public async Task