aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/ConnectService.cs45
-rw-r--r--MediaBrowser.Api/Devices/DeviceService.cs45
-rw-r--r--MediaBrowser.Api/Session/SessionsService.cs4
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs4
-rw-r--r--MediaBrowser.Controller/Connect/ConnectUser.cs1
-rw-r--r--MediaBrowser.Controller/Connect/IConnectManager.cs25
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs17
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj9
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj6
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectionResult.cs6
-rw-r--r--MediaBrowser.Model/ApiClient/ConnectionState.cs3
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs7
-rw-r--r--MediaBrowser.Model/ApiClient/IClientWebSocket.cs54
-rw-r--r--MediaBrowser.Model/ApiClient/IConnectionManager.cs12
-rw-r--r--MediaBrowser.Model/ApiClient/ServerInfo.cs1
-rw-r--r--MediaBrowser.Model/Connect/ConnectAuthorization.cs11
-rw-r--r--MediaBrowser.Model/Connect/UserLinkType.cs4
-rw-r--r--MediaBrowser.Model/Devices/DeviceInfo.cs28
-rw-r--r--MediaBrowser.Model/Devices/DeviceOptions.cs17
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs1
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj3
-rw-r--r--MediaBrowser.Server.Implementations/Connect/ConnectData.cs17
-rw-r--r--MediaBrowser.Server.Implementations/Connect/ConnectManager.cs359
-rw-r--r--MediaBrowser.Server.Implementations/Connect/Responses.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Devices/DeviceManager.cs41
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json10
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs24
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs4
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Model.Signed.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
34 files changed, 681 insertions, 114 deletions
diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs
index 9ea75d4ac..3a863316b 100644
--- a/MediaBrowser.Api/ConnectService.cs
+++ b/MediaBrowser.Api/ConnectService.cs
@@ -1,6 +1,8 @@
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Connect;
using ServiceStack;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Api
@@ -22,6 +24,30 @@ namespace MediaBrowser.Api
public string Id { get; set; }
}
+ [Route("/Connect/Invite", "POST", Summary = "Creates a Connect link for a user")]
+ public class CreateConnectInvite : IReturn<UserLinkResult>
+ {
+ [ApiMember(Name = "ConnectUsername", Description = "Connect username", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string ConnectUsername { get; set; }
+
+ [ApiMember(Name = "SendingUserId", Description = "Sending User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string SendingUserId { get; set; }
+ }
+
+
+ [Route("/Connect/Pending", "GET", Summary = "Creates a Connect link for a user")]
+ public class GetPendingGuests : IReturn<List<ConnectAuthorization>>
+ {
+ }
+
+
+ [Route("/Connect/Pending", "DELETE", Summary = "Deletes a Connect link for a user")]
+ public class DeleteAuthorization : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Authorization Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
[Authenticated(Roles = "Admin")]
public class ConnectService : BaseApiService
{
@@ -37,11 +63,30 @@ namespace MediaBrowser.Api
return _connectManager.LinkUser(request.Id, request.ConnectUsername);
}
+ public object Post(CreateConnectInvite request)
+ {
+ return _connectManager.InviteUser(request.SendingUserId, request.ConnectUsername);
+ }
+
public void Delete(DeleteConnectLink request)
{
var task = _connectManager.RemoveLink(request.Id);
Task.WaitAll(task);
}
+
+ public async Task<object> Get(GetPendingGuests request)
+ {
+ var result = await _connectManager.GetPendingGuests().ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+
+ public void Delete(DeleteAuthorization request)
+ {
+ var task = _connectManager.CancelAuthorization(request.Id);
+
+ Task.WaitAll(task);
+ }
}
}
diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs
index 87419e440..bb8d8eda3 100644
--- a/MediaBrowser.Api/Devices/DeviceService.cs
+++ b/MediaBrowser.Api/Devices/DeviceService.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Session;
using ServiceStack;
using ServiceStack.Web;
using System.Collections.Generic;
@@ -49,6 +50,27 @@ namespace MediaBrowser.Api.Devices
public Stream RequestStream { get; set; }
}
+ [Route("/Devices/Info", "GET", Summary = "Gets device info")]
+ public class GetDeviceInfo : IReturn<DeviceInfo>
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Devices/Capabilities", "GET", Summary = "Gets device capabilities")]
+ public class GetDeviceCapabilities : IReturn<ClientCapabilities>
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Devices/Options", "POST", Summary = "Updates device options")]
+ public class PostDeviceOptions : DeviceOptions, IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
[Authenticated]
public class DeviceService : BaseApiService
{
@@ -59,6 +81,27 @@ namespace MediaBrowser.Api.Devices
_deviceManager = deviceManager;
}
+ public void Post(PostDeviceOptions request)
+ {
+ var task = _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions
+ {
+ CustomName = request.CustomName,
+ CameraUploadPath = request.CameraUploadPath
+ });
+
+ Task.WaitAll(task);
+ }
+
+ public object Get(GetDeviceInfo request)
+ {
+ return ToOptimizedResult(_deviceManager.GetDevice(request.Id));
+ }
+
+ public object Get(GetDeviceCapabilities request)
+ {
+ return ToOptimizedResult(_deviceManager.GetCapabilities(request.Id));
+ }
+
public object Get(GetDevices request)
{
var devices = _deviceManager.GetDevices();
@@ -67,7 +110,7 @@ namespace MediaBrowser.Api.Devices
{
var val = request.SupportsContentUploading.Value;
- devices = devices.Where(i => i.Capabilities.SupportsContentUploading == val);
+ devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val);
}
return ToOptimizedResult(devices.ToList());
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs
index 014bedbd9..74dccb7af 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Session/SessionsService.cs
@@ -499,9 +499,9 @@ namespace MediaBrowser.Api.Session
}
_sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
{
- PlayableMediaTypes = request.PlayableMediaTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
+ PlayableMediaTypes = (request.PlayableMediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
- SupportedCommands = request.SupportedCommands.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
+ SupportedCommands = (request.SupportedCommands ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
SupportsMediaControl = request.SupportsMediaControl,
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index ad7aad1d3..09bf6e232 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -129,7 +129,7 @@ namespace MediaBrowser.Common.Net
public enum CacheMode
{
- None = 1,
- Unconditional = 2
+ None = 0,
+ Unconditional = 1
}
}
diff --git a/MediaBrowser.Controller/Connect/ConnectUser.cs b/MediaBrowser.Controller/Connect/ConnectUser.cs
index 389330cec..c6a9eba55 100644
--- a/MediaBrowser.Controller/Connect/ConnectUser.cs
+++ b/MediaBrowser.Controller/Connect/ConnectUser.cs
@@ -7,6 +7,7 @@ namespace MediaBrowser.Controller.Connect
public string Name { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
+ public string ImageUrl { get; set; }
}
public class ConnectUserQuery
diff --git a/MediaBrowser.Controller/Connect/IConnectManager.cs b/MediaBrowser.Controller/Connect/IConnectManager.cs
index 7c1e14c30..afb61cb23 100644
--- a/MediaBrowser.Controller/Connect/IConnectManager.cs
+++ b/MediaBrowser.Controller/Connect/IConnectManager.cs
@@ -1,4 +1,6 @@
-using System.Threading.Tasks;
+using MediaBrowser.Model.Connect;
+using System.Collections.Generic;
+using System.Threading.Tasks;
namespace MediaBrowser.Controller.Connect
{
@@ -24,5 +26,26 @@ namespace MediaBrowser.Controller.Connect
/// <param name="userId">The user identifier.</param>
/// <returns>Task.</returns>
Task RemoveLink(string userId);
+
+ /// <summary>
+ /// Invites the user.
+ /// </summary>
+ /// <param name="sendingUserId">The sending user identifier.</param>
+ /// <param name="connectUsername">The connect username.</param>
+ /// <returns>Task&lt;UserLinkResult&gt;.</returns>
+ Task<UserLinkResult> InviteUser(string sendingUserId, string connectUsername);
+
+ /// <summary>
+ /// Gets the pending guests.
+ /// </summary>
+ /// <returns>Task&lt;List&lt;ConnectAuthorization&gt;&gt;.</returns>
+ Task<List<ConnectAuthorization>> GetPendingGuests();
+
+ /// <summary>
+ /// Cancels the authorization.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <returns>Task.</returns>
+ Task CancelAuthorization(string id);
}
}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index e66de42bb..af184e6e9 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -1,5 +1,7 @@
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Session;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
@@ -9,6 +11,11 @@ namespace MediaBrowser.Controller.Devices
public interface IDeviceManager
{
/// <summary>
+ /// Occurs when [device options updated].
+ /// </summary>
+ event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
+
+ /// <summary>
/// Registers the device.
/// </summary>
/// <param name="reportedId">The reported identifier.</param>
@@ -16,7 +23,7 @@ namespace MediaBrowser.Controller.Devices
/// <param name="appName">Name of the application.</param>
/// <param name="usedByUserId">The used by user identifier.</param>
/// <returns>Task.</returns>
- Task RegisterDevice(string reportedId, string name, string appName, string usedByUserId);
+ Task<DeviceInfo> RegisterDevice(string reportedId, string name, string appName, string usedByUserId);
/// <summary>
/// Saves the capabilities.
@@ -41,6 +48,14 @@ namespace MediaBrowser.Controller.Devices
DeviceInfo GetDevice(string id);
/// <summary>
+ /// Updates the device information.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>Task.</returns>
+ Task UpdateDeviceInfo(string id, DeviceOptions options);
+
+ /// <summary>
/// Gets the devices.
/// </summary>
/// <returns>IEnumerable&lt;DeviceInfo&gt;.</returns>
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index f8d2de85a..dfd5eb2bb 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -92,6 +92,9 @@
<Compile Include="..\MediaBrowser.Model\ApiClient\IApiClient.cs">
<Link>ApiClient\IApiClient.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\ApiClient\IClientWebSocket.cs">
+ <Link>ApiClient\IClientWebSocket.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\ApiClient\IConnectionManager.cs">
<Link>ApiClient\IConnectionManager.cs</Link>
</Compile>
@@ -212,6 +215,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs">
<Link>Configuration\XbmcMetadataOptions.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthorization.cs">
+ <Link>Connect\ConnectAuthorization.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Connect\UserLinkType.cs">
<Link>Connect\UserLinkType.cs</Link>
</Compile>
@@ -221,6 +227,9 @@
<Compile Include="..\MediaBrowser.Model\Devices\DeviceInfo.cs">
<Link>Devices\DeviceInfo.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
+ <Link>Devices\DeviceOptions.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
<Link>Devices\DevicesOptions.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 901862740..c8ac202ab 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -181,6 +181,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs">
<Link>Configuration\XbmcMetadataOptions.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthorization.cs">
+ <Link>Connect\ConnectAuthorization.cs</Link>
+ </Compile>
<Compile Include="..\mediabrowser.model\connect\UserLinkType.cs">
<Link>Connect\UserLinkType.cs</Link>
</Compile>
@@ -190,6 +193,9 @@
<Compile Include="..\MediaBrowser.Model\Devices\DeviceInfo.cs">
<Link>Devices\DeviceInfo.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
+ <Link>Devices\DeviceOptions.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
<Link>Devices\DevicesOptions.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model/ApiClient/ConnectionResult.cs b/MediaBrowser.Model/ApiClient/ConnectionResult.cs
index 0b6c0bfe9..86c94c3d5 100644
--- a/MediaBrowser.Model/ApiClient/ConnectionResult.cs
+++ b/MediaBrowser.Model/ApiClient/ConnectionResult.cs
@@ -1,15 +1,17 @@
-
+using System.Collections.Generic;
+
namespace MediaBrowser.Model.ApiClient
{
public class ConnectionResult
{
public ConnectionState State { get; set; }
- public ServerInfo ServerInfo { get; set; }
+ public List<ServerInfo> Servers { get; set; }
public IApiClient ApiClient { get; set; }
public ConnectionResult()
{
State = ConnectionState.Unavailable;
+ Servers = new List<ServerInfo>();
}
}
}
diff --git a/MediaBrowser.Model/ApiClient/ConnectionState.cs b/MediaBrowser.Model/ApiClient/ConnectionState.cs
index 9374c77f6..63e156eb1 100644
--- a/MediaBrowser.Model/ApiClient/ConnectionState.cs
+++ b/MediaBrowser.Model/ApiClient/ConnectionState.cs
@@ -4,6 +4,7 @@ namespace MediaBrowser.Model.ApiClient
{
Unavailable = 1,
ServerSignIn = 2,
- SignedIn = 3
+ SignedIn = 3,
+ ServerSelection = 4
}
} \ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 1d2145b3a..13088945a 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -1350,5 +1350,12 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <returns>Task&lt;DevicesOptions&gt;.</returns>
Task<DevicesOptions> GetDevicesOptions();
+
+ /// <summary>
+ /// Opens the web socket.
+ /// </summary>
+ /// <param name="webSocketFactory">The web socket factory.</param>
+ /// <param name="keepAliveTimerMs">The keep alive timer ms.</param>
+ void OpenWebSocket(Func<IClientWebSocket> webSocketFactory, int keepAliveTimerMs = 60000);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/IClientWebSocket.cs b/MediaBrowser.Model/ApiClient/IClientWebSocket.cs
new file mode 100644
index 000000000..ca3a761d4
--- /dev/null
+++ b/MediaBrowser.Model/ApiClient/IClientWebSocket.cs
@@ -0,0 +1,54 @@
+using MediaBrowser.Model.Net;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Model.ApiClient
+{
+ /// <summary>
+ /// Interface IClientWebSocket
+ /// </summary>
+ public interface IClientWebSocket : IDisposable
+ {
+ /// <summary>
+ /// Occurs when [closed].
+ /// </summary>
+ event EventHandler Closed;
+
+ /// <summary>
+ /// Gets or sets the state.
+ /// </summary>
+ /// <value>The state.</value>
+ WebSocketState State { get; }
+
+ /// <summary>
+ /// Connects the async.
+ /// </summary>
+ /// <param name="url">The URL.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task ConnectAsync(string url, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets or sets the receive action.
+ /// </summary>
+ /// <value>The receive action.</value>
+ Action<byte[]> OnReceiveBytes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the on receive.
+ /// </summary>
+ /// <value>The on receive.</value>
+ Action<string> OnReceive { get; set; }
+
+ /// <summary>
+ /// Sends the async.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs
index 03d3472d2..d76ef67d2 100644
--- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs
+++ b/MediaBrowser.Model/ApiClient/IConnectionManager.cs
@@ -53,5 +53,17 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <returns>Task&lt;ConnectionResult&gt;.</returns>
Task<ConnectionResult> Logout();
+
+ /// <summary>
+ /// Logins to connect.
+ /// </summary>
+ /// <returns>Task.</returns>
+ Task LoginToConnect(string username, string password);
+
+ /// <summary>
+ /// Gets the active api client instance
+ /// </summary>
+ [Obsolete]
+ IApiClient CurrentApiClient { get; }
}
}
diff --git a/MediaBrowser.Model/ApiClient/ServerInfo.cs b/MediaBrowser.Model/ApiClient/ServerInfo.cs
index 0656337ba..af46cc660 100644
--- a/MediaBrowser.Model/ApiClient/ServerInfo.cs
+++ b/MediaBrowser.Model/ApiClient/ServerInfo.cs
@@ -12,6 +12,7 @@ namespace MediaBrowser.Model.ApiClient
public String UserId { get; set; }
public String AccessToken { get; set; }
public List<WakeOnLanInfo> WakeOnLanInfos { get; set; }
+ public DateTime DateLastAccessed { get; set; }
public ServerInfo()
{
diff --git a/MediaBrowser.Model/Connect/ConnectAuthorization.cs b/MediaBrowser.Model/Connect/ConnectAuthorization.cs
new file mode 100644
index 000000000..329d805f1
--- /dev/null
+++ b/MediaBrowser.Model/Connect/ConnectAuthorization.cs
@@ -0,0 +1,11 @@
+
+namespace MediaBrowser.Model.Connect
+{
+ public class ConnectAuthorization
+ {
+ public string ConnectUserId { get; set; }
+ public string UserName { get; set; }
+ public string ImageUrl { get; set; }
+ public string Id { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Connect/UserLinkType.cs b/MediaBrowser.Model/Connect/UserLinkType.cs
index e8ea15a4e..4ac5bfde1 100644
--- a/MediaBrowser.Model/Connect/UserLinkType.cs
+++ b/MediaBrowser.Model/Connect/UserLinkType.cs
@@ -6,10 +6,10 @@ namespace MediaBrowser.Model.Connect
/// <summary>
/// The linked user
/// </summary>
- LinkedUser = 1,
+ LinkedUser = 0,
/// <summary>
/// The guest
/// </summary>
- Guest = 2
+ Guest = 1
}
}
diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs
index fce3b02f6..e5efe9f60 100644
--- a/MediaBrowser.Model/Devices/DeviceInfo.cs
+++ b/MediaBrowser.Model/Devices/DeviceInfo.cs
@@ -1,15 +1,35 @@
-using System;
-using MediaBrowser.Model.Session;
+using MediaBrowser.Model.Session;
+using System;
namespace MediaBrowser.Model.Devices
{
public class DeviceInfo
{
/// <summary>
- /// Gets or sets the name.
+ /// Gets or sets the name of the reported.
+ /// </summary>
+ /// <value>The name of the reported.</value>
+ public string ReportedName { get; set; }
+ /// <summary>
+ /// Gets or sets the name of the custom.
+ /// </summary>
+ /// <value>The name of the custom.</value>
+ public string CustomName { get; set; }
+ /// <summary>
+ /// Gets or sets the camera upload path.
+ /// </summary>
+ /// <value>The camera upload path.</value>
+ public string CameraUploadPath { get; set; }
+
+ /// <summary>
+ /// Gets the name.
/// </summary>
/// <value>The name.</value>
- public string Name { get; set; }
+ public string Name
+ {
+ get { return string.IsNullOrEmpty(CustomName) ? ReportedName : CustomName; }
+ }
+
/// <summary>
/// Gets or sets the identifier.
/// </summary>
diff --git a/MediaBrowser.Model/Devices/DeviceOptions.cs b/MediaBrowser.Model/Devices/DeviceOptions.cs
new file mode 100644
index 000000000..2524a2f99
--- /dev/null
+++ b/MediaBrowser.Model/Devices/DeviceOptions.cs
@@ -0,0 +1,17 @@
+
+namespace MediaBrowser.Model.Devices
+{
+ public class DeviceOptions
+ {
+ /// <summary>
+ /// Gets or sets the name of the custom.
+ /// </summary>
+ /// <value>The name of the custom.</value>
+ public string CustomName { get; set; }
+ /// <summary>
+ /// Gets or sets the camera upload path.
+ /// </summary>
+ /// <value>The camera upload path.</value>
+ public string CameraUploadPath { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index e51edae1b..80fda4bc2 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -232,6 +232,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the recursive unplayed item count.
/// </summary>
/// <value>The recursive unplayed item count.</value>
+ [Obsolete]
public int? RecursiveUnplayedItemCount { get; set; }
/// <summary>
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index e1ce7ecca..fa0c2d466 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -66,6 +66,7 @@
<Compile Include="ApiClient\HttpResponseEventArgs.cs" />
<Compile Include="ApiClient\IApiClient.cs" />
<Compile Include="ApiClient\ApiClientExtensions.cs" />
+ <Compile Include="ApiClient\IClientWebSocket.cs" />
<Compile Include="ApiClient\IConnectionManager.cs" />
<Compile Include="ApiClient\IDevice.cs" />
<Compile Include="ApiClient\IServerEvents.cs" />
@@ -95,7 +96,9 @@
<Compile Include="Configuration\PeopleMetadataOptions.cs" />
<Compile Include="Configuration\XbmcMetadataOptions.cs" />
<Compile Include="Configuration\SubtitlePlaybackMode.cs" />
+ <Compile Include="Connect\ConnectAuthorization.cs" />
<Compile Include="Connect\UserLinkType.cs" />
+ <Compile Include="Devices\DeviceOptions.cs" />
<Compile Include="Devices\LocalFileInfo.cs" />
<Compile Include="Devices\DeviceInfo.cs" />
<Compile Include="Devices\DevicesOptions.cs" />
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectData.cs b/MediaBrowser.Server.Implementations/Connect/ConnectData.cs
index dfbeccd4d..e26fdc607 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectData.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectData.cs
@@ -1,4 +1,4 @@
-using System;
+using MediaBrowser.Model.Connect;
using System.Collections.Generic;
namespace MediaBrowser.Server.Implementations.Connect
@@ -20,22 +20,11 @@ namespace MediaBrowser.Server.Implementations.Connect
/// Gets or sets the authorizations.
/// </summary>
/// <value>The authorizations.</value>
- public List<ConnectAuthorization> Authorizations { get; set; }
+ public List<ConnectAuthorization> PendingAuthorizations { get; set; }
public ConnectData()
{
- Authorizations = new List<ConnectAuthorization>();
- }
- }
-
- public class ConnectAuthorization
- {
- public string LocalUserId { get; set; }
- public string AccessToken { get; set; }
-
- public ConnectAuthorization()
- {
- AccessToken = new Guid().ToString("N");
+ PendingAuthorizations = new List<ConnectAuthorization>();
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
index a4535a57c..4e3bbbcb2 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
@@ -5,8 +5,10 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Connect;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
@@ -24,7 +26,7 @@ namespace MediaBrowser.Server.Implementations.Connect
{
public class ConnectManager : IConnectManager
{
- private SemaphoreSlim _operationLock = new SemaphoreSlim(1,1);
+ private readonly SemaphoreSlim _operationLock = new SemaphoreSlim(1, 1);
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
@@ -34,6 +36,7 @@ namespace MediaBrowser.Server.Implementations.Connect
private readonly IServerApplicationHost _appHost;
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
+ private readonly IProviderManager _providerManager;
private ConnectData _data = new ConnectData();
@@ -90,7 +93,7 @@ namespace MediaBrowser.Server.Implementations.Connect
IEncryptionManager encryption,
IHttpClient httpClient,
IServerApplicationHost appHost,
- IServerConfigurationManager config, IUserManager userManager)
+ IServerConfigurationManager config, IUserManager userManager, IProviderManager providerManager)
{
_logger = logger;
_appPaths = appPaths;
@@ -100,6 +103,7 @@ namespace MediaBrowser.Server.Implementations.Connect
_appHost = appHost;
_config = config;
_userManager = userManager;
+ _providerManager = providerManager;
LoadCachedData();
}
@@ -165,13 +169,15 @@ namespace MediaBrowser.Server.Implementations.Connect
}
await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false);
+
+ await RefreshUserInfosInternal(CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error registering with Connect", ex);
}
}
-
+
private async Task CreateServerRegistration(string wanApiAddress)
{
var url = "Servers";
@@ -181,7 +187,7 @@ namespace MediaBrowser.Server.Implementations.Connect
{
{"name", _appHost.FriendlyName},
{"url", wanApiAddress},
- {"systemid", _appHost.SystemId}
+ {"systemId", _appHost.SystemId}
};
using (var stream = await _httpClient.Post(url, postData, CancellationToken.None).ConfigureAwait(false))
@@ -211,7 +217,7 @@ namespace MediaBrowser.Server.Implementations.Connect
{
{"name", _appHost.FriendlyName},
{"url", wanApiAddress},
- {"systemid", _appHost.SystemId}
+ {"systemId", _appHost.SystemId}
});
SetServerAccessToken(options);
@@ -222,6 +228,7 @@ namespace MediaBrowser.Server.Implementations.Connect
}
}
+ private readonly object _dataFileLock = new object();
private string CacheFilePath
{
get { return Path.Combine(_appPaths.DataPath, "connect.txt"); }
@@ -239,7 +246,10 @@ namespace MediaBrowser.Server.Implementations.Connect
var encrypted = _encryption.EncryptString(json);
- File.WriteAllText(path, encrypted, Encoding.UTF8);
+ lock (_dataFileLock)
+ {
+ File.WriteAllText(path, encrypted, Encoding.UTF8);
+ }
}
catch (Exception ex)
{
@@ -253,11 +263,14 @@ namespace MediaBrowser.Server.Implementations.Connect
try
{
- var encrypted = File.ReadAllText(path, Encoding.UTF8);
+ lock (_dataFileLock)
+ {
+ var encrypted = File.ReadAllText(path, Encoding.UTF8);
- var json = _encryption.DecryptString(encrypted);
+ var json = _encryption.DecryptString(encrypted);
- _data = _json.DeserializeFromString<ConnectData>(json);
+ _data = _json.DeserializeFromString<ConnectData>(json);
+ }
}
catch (IOException)
{
@@ -288,6 +301,20 @@ namespace MediaBrowser.Server.Implementations.Connect
public async Task<UserLinkResult> LinkUser(string userId, string connectUsername)
{
+ await _operationLock.WaitAsync().ConfigureAwait(false);
+
+ try
+ {
+ return await LinkUserInternal(userId, connectUsername).ConfigureAwait(false);
+ }
+ finally
+ {
+ _operationLock.Release();
+ }
+ }
+
+ private async Task<UserLinkResult> LinkUserInternal(string userId, string connectUsername)
+ {
if (string.IsNullOrWhiteSpace(connectUsername))
{
throw new ArgumentNullException("connectUsername");
@@ -350,56 +377,98 @@ namespace MediaBrowser.Server.Implementations.Connect
await user.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
+ user.Configuration.SyncConnectImage = user.ConnectLinkType == UserLinkType.Guest;
+ user.Configuration.SyncConnectName = user.ConnectLinkType == UserLinkType.Guest;
+ _userManager.UpdateConfiguration(user, user.Configuration);
+
+ await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false);
+
return result;
}
-
- public Task RemoveLink(string userId)
+
+ public async Task<UserLinkResult> InviteUser(string sendingUserId, string connectUsername)
{
- var user = GetUser(userId);
+ await _operationLock.WaitAsync().ConfigureAwait(false);
- return RemoveLink(user, user.ConnectUserId);
+ try
+ {
+ return await InviteUserInternal(sendingUserId, connectUsername).ConfigureAwait(false);
+ }
+ finally
+ {
+ _operationLock.Release();
+ }
}
- private async Task RemoveLink(User user, string connectUserId)
+ private async Task<UserLinkResult> InviteUserInternal(string sendingUserId, string connectUsername)
{
- if (!string.IsNullOrWhiteSpace(connectUserId))
+ if (string.IsNullOrWhiteSpace(connectUsername))
{
- var url = GetConnectUrl("ServerAuthorizations");
+ throw new ArgumentNullException("connectUsername");
+ }
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None
- };
+ var connectUser = await GetConnectUser(new ConnectUserQuery
+ {
+ Name = connectUsername
- var postData = new Dictionary<string, string>
- {
- {"serverId", ConnectServerId},
- {"userId", connectUserId}
- };
+ }, CancellationToken.None).ConfigureAwait(false);
+
+ if (!connectUser.IsActive)
+ {
+ throw new ArgumentException("The Media Browser account has been disabled.");
+ }
- options.SetPostData(postData);
+ var url = GetConnectUrl("ServerAuthorizations");
- SetServerAccessToken(options);
+ var options = new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = CancellationToken.None
+ };
- try
- {
- // No need to examine the response
- using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content)
- {
- }
- }
- catch (HttpException ex)
- {
- // If connect says the auth doesn't exist, we can handle that gracefully since this is a remove operation
+ var accessToken = Guid.NewGuid().ToString("N");
+ var sendingUser = GetUser(sendingUserId);
- if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
- {
- throw;
- }
+ var postData = new Dictionary<string, string>
+ {
+ {"serverId", ConnectServerId},
+ {"userId", connectUser.Id},
+ {"userType", "Guest"},
+ {"accessToken", accessToken},
+ {"requesterUserName", sendingUser.ConnectUserName}
+ };
- _logger.Debug("Connect returned a 404 when removing a user auth link. Handling it.");
- }
+ options.SetPostData(postData);
+
+ SetServerAccessToken(options);
+
+ var result = new UserLinkResult();
+
+ // No need to examine the response
+ using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
+ {
+ var response = _json.DeserializeFromStream<ServerUserAuthorizationResponse>(stream);
+
+ result.IsPending = string.Equals(response.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase);
+ }
+
+ await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false);
+
+ return result;
+ }
+
+ public Task RemoveLink(string userId)
+ {
+ var user = GetUser(userId);
+
+ return RemoveLink(user, user.ConnectUserId);
+ }
+
+ private async Task RemoveLink(User user, string connectUserId)
+ {
+ if (!string.IsNullOrWhiteSpace(connectUserId))
+ {
+ await CancelAuthorizationByConnectUserId(connectUserId).ConfigureAwait(false);
}
user.ConnectAccessKey = null;
@@ -472,24 +541,19 @@ namespace MediaBrowser.Server.Implementations.Connect
{
var url = GetConnectUrl("ServerAuthorizations");
+ url += "?serverId=" + ConnectServerId;
+
var options = new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken
};
- var postData = new Dictionary<string, string>
- {
- {"serverId", ConnectServerId}
- };
-
- options.SetPostData(postData);
-
SetServerAccessToken(options);
try
{
- using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
+ using (var stream = (await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)).Content)
{
var list = _json.DeserializeFromStream<List<ServerUserAuthorizationResponse>>(stream);
@@ -521,10 +585,11 @@ namespace MediaBrowser.Server.Implementations.Connect
user.ConnectUserName = null;
await _userManager.UpdateUser(user).ConfigureAwait(false);
-
+
if (user.ConnectLinkType == UserLinkType.Guest)
{
- await _userManager.DeleteUser(user).ConfigureAwait(false);
+ _logger.Debug("Deleting guest user {0}", user.Name);
+ //await _userManager.DeleteUser(user).ConfigureAwait(false);
}
}
else
@@ -544,19 +609,195 @@ namespace MediaBrowser.Server.Implementations.Connect
users = _userManager.Users.ToList();
+ var pending = new List<ConnectAuthorization>();
+
// TODO: Handle newly added guests that we don't know about
foreach (var connectEntry in list)
{
- if (string.Equals(connectEntry.UserType, "guest", StringComparison.OrdinalIgnoreCase) &&
- string.Equals(connectEntry.AcceptStatus, "accepted", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(connectEntry.UserType, "guest", StringComparison.OrdinalIgnoreCase))
+ {
+ if (string.Equals(connectEntry.AcceptStatus, "accepted", StringComparison.OrdinalIgnoreCase))
+ {
+ var user = users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectEntry.UserId, StringComparison.OrdinalIgnoreCase));
+
+ if (user == null)
+ {
+ // Add user
+ }
+ }
+ else if (string.Equals(connectEntry.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase))
+ {
+ pending.Add(new ConnectAuthorization
+ {
+ ConnectUserId = connectEntry.UserId,
+ ImageUrl = connectEntry.ImageUrl,
+ UserName = connectEntry.UserName,
+ Id = connectEntry.Id
+ });
+ }
+ }
+ }
+
+ _data.PendingAuthorizations = pending;
+ CacheData();
+ }
+
+ public async Task RefreshUserInfos(CancellationToken cancellationToken)
+ {
+ await _operationLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ await RefreshUserInfosInternal(cancellationToken).ConfigureAwait(false);
+ }
+ finally
+ {
+ _operationLock.Release();
+ }
+ }
+
+ private readonly SemaphoreSlim _connectImageSemaphore = new SemaphoreSlim(5, 5);
+
+ private async Task RefreshUserInfosInternal(CancellationToken cancellationToken)
+ {
+ var users = _userManager.Users
+ .Where(i => !string.IsNullOrEmpty(i.ConnectUserId) &&
+ (i.Configuration.SyncConnectImage || i.Configuration.SyncConnectName))
+ .ToList();
+
+ foreach (var user in users)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var connectUser = await GetConnectUser(new ConnectUserQuery
{
- var user = users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectEntry.UserId, StringComparison.OrdinalIgnoreCase));
+ Id = user.ConnectUserId
- if (user == null)
+ }, cancellationToken).ConfigureAwait(false);
+
+ if (user.Configuration.SyncConnectName)
+ {
+ var changed = !string.Equals(connectUser.Name, user.Name, StringComparison.OrdinalIgnoreCase);
+
+ if (changed)
{
- // Add user
+ await user.Rename(connectUser.Name).ConfigureAwait(false);
}
}
+
+ if (user.Configuration.SyncConnectImage)
+ {
+ var imageUrl = connectUser.ImageUrl;
+
+ if (!string.IsNullOrWhiteSpace(imageUrl))
+ {
+ var changed = false;
+
+ if (!user.HasImage(ImageType.Primary))
+ {
+ changed = true;
+ }
+ else
+ {
+ using (var response = await _httpClient.SendAsync(new HttpRequestOptions
+ {
+ Url = imageUrl,
+ CancellationToken = cancellationToken,
+ BufferContent = false
+
+ }, "HEAD").ConfigureAwait(false))
+ {
+ var length = response.ContentLength;
+
+ if (length != new FileInfo(user.GetImageInfo(ImageType.Primary, 0).Path).Length)
+ {
+ changed = true;
+ }
+ }
+ }
+
+ if (changed)
+ {
+ await _providerManager.SaveImage(user, imageUrl, _connectImageSemaphore, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
+
+ await user.RefreshMetadata(new MetadataRefreshOptions
+ {
+ ForceSave = true,
+
+ }, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+ }
+ }
+
+ public async Task<List<ConnectAuthorization>> GetPendingGuests()
+ {
+ return _data.PendingAuthorizations.ToList();
+ }
+
+ public async Task CancelAuthorization(string id)
+ {
+ await _operationLock.WaitAsync().ConfigureAwait(false);
+
+ try
+ {
+ await CancelAuthorizationInternal(id).ConfigureAwait(false);
+ }
+ finally
+ {
+ _operationLock.Release();
+ }
+ }
+
+ private async Task CancelAuthorizationInternal(string id)
+ {
+ var connectUserId = _data.PendingAuthorizations
+ .First(i => string.Equals(i.Id, id, StringComparison.Ordinal))
+ .ConnectUserId;
+
+ await CancelAuthorizationByConnectUserId(connectUserId).ConfigureAwait(false);
+
+ await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false);
+ }
+
+ private async Task CancelAuthorizationByConnectUserId(string connectUserId)
+ {
+ var url = GetConnectUrl("ServerAuthorizations");
+
+ var options = new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = CancellationToken.None
+ };
+
+ var postData = new Dictionary<string, string>
+ {
+ {"serverId", ConnectServerId},
+ {"userId", connectUserId}
+ };
+
+ options.SetPostData(postData);
+
+ SetServerAccessToken(options);
+
+ try
+ {
+ // No need to examine the response
+ using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content)
+ {
+ }
+ }
+ catch (HttpException ex)
+ {
+ // If connect says the auth doesn't exist, we can handle that gracefully since this is a remove operation
+
+ if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
+ {
+ throw;
+ }
+
+ _logger.Debug("Connect returned a 404 when removing a user auth link. Handling it.");
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Connect/Responses.cs b/MediaBrowser.Server.Implementations/Connect/Responses.cs
index eeb56d1c9..862e79559 100644
--- a/MediaBrowser.Server.Implementations/Connect/Responses.cs
+++ b/MediaBrowser.Server.Implementations/Connect/Responses.cs
@@ -36,5 +36,7 @@ namespace MediaBrowser.Server.Implementations.Connect
public bool IsActive { get; set; }
public string AcceptStatus { get; set; }
public string UserType { get; set; }
+ public string ImageUrl { get; set; }
+ public string UserName { get; set; }
}
}
diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
index 6d4238bdf..e2c729b2d 100644
--- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
+++ b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
@@ -1,8 +1,11 @@
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Events;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
@@ -19,24 +22,31 @@ namespace MediaBrowser.Server.Implementations.Devices
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IConfigurationManager _config;
-
- public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IConfigurationManager config)
+ private readonly ILogger _logger;
+
+ /// <summary>
+ /// Occurs when [device options updated].
+ /// </summary>
+ public event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
+
+ public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IConfigurationManager config, ILogger logger)
{
_repo = repo;
_userManager = userManager;
_fileSystem = fileSystem;
_libraryMonitor = libraryMonitor;
_config = config;
+ _logger = logger;
}
- public Task RegisterDevice(string reportedId, string name, string appName, string usedByUserId)
+ public async Task<DeviceInfo> RegisterDevice(string reportedId, string name, string appName, string usedByUserId)
{
var device = GetDevice(reportedId) ?? new DeviceInfo
{
Id = reportedId
};
- device.Name = name;
+ device.ReportedName = name;
device.AppName = appName;
if (!string.IsNullOrWhiteSpace(usedByUserId))
@@ -49,7 +59,9 @@ namespace MediaBrowser.Server.Implementations.Devices
device.DateLastModified = DateTime.UtcNow;
- return _repo.SaveDevice(device);
+ await _repo.SaveDevice(device).ConfigureAwait(false);
+
+ return device;
}
public Task SaveCapabilities(string reportedId, ClientCapabilities capabilities)
@@ -114,10 +126,13 @@ namespace MediaBrowser.Server.Implementations.Devices
private string GetUploadPath(string deviceId)
{
- var config = _config.GetUploadOptions();
-
var device = GetDevice(deviceId);
+ if (!string.IsNullOrWhiteSpace(device.CameraUploadPath))
+ {
+ return device.CameraUploadPath;
+ }
+ var config = _config.GetUploadOptions();
if (!string.IsNullOrWhiteSpace(config.CameraUploadPath))
{
return config.CameraUploadPath;
@@ -132,6 +147,18 @@ namespace MediaBrowser.Server.Implementations.Devices
return path;
}
+
+ public async Task UpdateDeviceInfo(string id, DeviceOptions options)
+ {
+ var device = GetDevice(id);
+
+ device.CustomName = options.CustomName;
+ device.CameraUploadPath = options.CameraUploadPath;
+
+ await _repo.SaveDevice(device).ConfigureAwait(false);
+
+ EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs<DeviceInfo>(device), _logger);
+ }
}
public class DevicesConfigStore : IConfigurationFactory
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 09e0e91f5..ce6224295 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -310,12 +310,23 @@ namespace MediaBrowser.Server.Implementations.IO
/// <param name="watcher">The watcher.</param>
private void DisposeWatcher(FileSystemWatcher watcher)
{
- Logger.Info("Stopping directory watching for path {0}", watcher.Path);
-
- watcher.EnableRaisingEvents = false;
- watcher.Dispose();
+ try
+ {
+ using (watcher)
+ {
+ Logger.Info("Stopping directory watching for path {0}", watcher.Path);
- RemoveWatcherFromList(watcher);
+ watcher.EnableRaisingEvents = false;
+ }
+ }
+ catch
+ {
+
+ }
+ finally
+ {
+ RemoveWatcherFromList(watcher);
+ }
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index b12ae32a8..768c29ce0 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -2,9 +2,7 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Localization;
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index d7a99638e..e576180c4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -75,7 +75,7 @@
"TabSecurity": "Security",
"ButtonAddUser": "Add User",
"ButtonAddLocalUser": "Add Local User",
- "ButtonInviteMediaBrowserUser": "Invite Media Browser User",
+ "ButtonInviteUser": "Invite User",
"ButtonSave": "Save",
"ButtonResetPassword": "Reset Password",
"LabelNewPassword": "New password:",
@@ -1225,5 +1225,11 @@
"LabelCameraUploadPath": "Camera upload path:",
"LabelCameraUploadPathHelp": "Select a custom upload path, if desired. If unspecified a default folder will be used.",
"LabelCreateCameraUploadSubfolder": "Create a subfolder for each device",
- "LabelCreateCameraUploadSubfolderHelp": "Specific folders can be assigned to a device by clicking on it from the Devices page."
+ "LabelCreateCameraUploadSubfolderHelp": "Specific folders can be assigned to a device by clicking on it from the Devices page.",
+ "LabelCustomDeviceDisplayName": "Display name:",
+ "LabelCustomDeviceDisplayNameHelp": "Supply a custom display name or leave empty to use the name reported by the device.",
+ "HeaderInviteUser": "Invite User",
+ "LabelConnectInviteHelp": "This is the username or email used to sign in to the Media Browser website.",
+ "HeaderInviteUserHelp": "Sharing your media with friends is easier than ever before with Media Browser Connect.",
+ "ButtonSendInvitation": "Send Invitation"
}
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index c16044bd3..21467ccda 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
@@ -129,6 +130,19 @@ namespace MediaBrowser.Server.Implementations.Session
_httpClient = httpClient;
_authRepo = authRepo;
_deviceManager = deviceManager;
+
+ _deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated;
+ }
+
+ void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<DeviceInfo> e)
+ {
+ foreach (var session in Sessions)
+ {
+ if (string.Equals(session.DeviceId, e.Argument.Id))
+ {
+ session.DeviceName = e.Argument.Name;
+ }
+ }
}
/// <summary>
@@ -397,6 +411,7 @@ namespace MediaBrowser.Server.Implementations.Session
try
{
SessionInfo connection;
+ DeviceInfo device = null;
if (!_activeConnections.TryGetValue(key, out connection))
{
@@ -421,10 +436,17 @@ namespace MediaBrowser.Server.Implementations.Session
if (!string.IsNullOrEmpty(deviceId))
{
var userIdString = userId.HasValue ? userId.Value.ToString("N") : null;
- await _deviceManager.RegisterDevice(deviceId, deviceName, clientType, userIdString).ConfigureAwait(false);
+ device = await _deviceManager.RegisterDevice(deviceId, deviceName, clientType, userIdString).ConfigureAwait(false);
}
}
+ device = device ?? _deviceManager.GetDevice(deviceId);
+
+ if (!string.IsNullOrEmpty(device.CustomName))
+ {
+ deviceName = device.CustomName;
+ }
+
connection.DeviceName = deviceName;
connection.UserId = userId;
connection.UserName = username;
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 8e41ef03e..2c9319e39 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -457,10 +457,10 @@ namespace MediaBrowser.ServerApplication
var encryptionManager = new EncryptionManager();
RegisterSingleInstance<IEncryptionManager>(encryptionManager);
- ConnectManager = new ConnectManager(LogManager.GetLogger("Connect"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager);
+ ConnectManager = new ConnectManager(LogManager.GetLogger("Connect"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager);
RegisterSingleInstance(ConnectManager);
- DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer), UserManager, FileSystemManager, LibraryMonitor, ConfigurationManager);
+ DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer), UserManager, FileSystemManager, LibraryMonitor, ConfigurationManager, LogManager.GetLogger("DeviceManager"));
RegisterSingleInstance(DeviceManager);
SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, ItemRepository, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager);
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index 6f9d320df..8d68f0c13 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.477</version>
+ <version>3.0.482</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.477" />
+ <dependency id="MediaBrowser.Common" version="3.0.482" />
<dependency id="NLog" version="3.1.0.0" />
<dependency id="SimpleInjector" version="2.5.2" />
<dependency id="sharpcompress" version="0.10.2" />
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index a2fa43075..90ae9dbac 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.477</version>
+ <version>3.0.482</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec
index cd18acd57..90f107af9 100644
--- a/Nuget/MediaBrowser.Model.Signed.nuspec
+++ b/Nuget/MediaBrowser.Model.Signed.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Model.Signed</id>
- <version>3.0.477</version>
+ <version>3.0.482</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index 988533e64..545f52e2c 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.477</version>
+ <version>3.0.482</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.477" />
+ <dependency id="MediaBrowser.Common" version="3.0.482" />
</dependencies>
</metadata>
<files>