Explorar el Código

restored request logging

LukePulverenti hace 12 años
padre
commit
4d083b618d

+ 118 - 0
MediaBrowser.Api/BaseApiService.cs

@@ -0,0 +1,118 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Connectivity;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Server.Implementations.HttpServer;
+using ServiceStack.Common.Web;
+using ServiceStack.ServiceHost;
+using System;
+
+namespace MediaBrowser.Api
+{
+    /// <summary>
+    /// Class BaseApiService
+    /// </summary>
+    [RequestFilter]
+    public class BaseApiService : BaseRestService
+    {
+    }
+
+    /// <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; }
+
+        /// <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)
+            {
+                var user = UserManager.GetUserById(new Guid(auth["UserId"]));
+
+                ClientType clientType;
+
+                Enum.TryParse(auth["Client"] ?? string.Empty, out clientType);
+
+                UserManager.LogUserActivity(user, clientType, auth["DeviceId"], auth["Device"] ?? string.Empty);
+            }
+        }
+
+        /// <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];
+            if (auth == null) return null;
+
+            var parts = auth.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
+            auth = auth.Substring(auth.IndexOf(' '));
+            parts = auth.Split(',');
+
+            var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            foreach (var item in parts)
+            {
+                var param = item.Trim().Split(new[] { '=' }, 2);
+                result.Add(param[0], param[1].Trim(new[] { '"' }));
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// A new shallow copy of this filter is used on every request.
+        /// </summary>
+        /// <returns>IHasRequestFilter.</returns>
+        public IHasRequestFilter Copy()
+        {
+            return this;
+        }
+
+        /// <summary>
+        /// Order in which Request Filters are executed.
+        /// &lt;0 Executed before global request filters
+        /// &gt;0 Executed after global request filters
+        /// </summary>
+        /// <value>The priority.</value>
+        public int Priority
+        {
+            get { return 0; }
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Api/EnvironmentService.cs

@@ -75,7 +75,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class EnvironmentService
     /// </summary>
-    public class EnvironmentService : BaseRestService
+    public class EnvironmentService : BaseApiService
     {
         /// <summary>
         /// The _network manager

+ 1 - 1
MediaBrowser.Api/Images/ImageService.cs

@@ -150,7 +150,7 @@ namespace MediaBrowser.Api.Images
     /// <summary>
     /// Class ImageService
     /// </summary>
-    public class ImageService : BaseRestService
+    public class ImageService : BaseApiService
     {
         /// <summary>
         /// The _user manager

+ 1 - 1
MediaBrowser.Api/Library/LibraryService.cs

@@ -98,7 +98,7 @@ namespace MediaBrowser.Api.Library
     /// <summary>
     /// Class LibraryService
     /// </summary>
-    public class LibraryService : BaseRestService
+    public class LibraryService : BaseApiService
     {
         /// <summary>
         /// The _app host

+ 1 - 1
MediaBrowser.Api/Library/LibraryStructureService.cs

@@ -129,7 +129,7 @@ namespace MediaBrowser.Api.Library
     /// <summary>
     /// Class LibraryStructureService
     /// </summary>
-    public class LibraryStructureService : BaseRestService
+    public class LibraryStructureService : BaseApiService
     {
         /// <summary>
         /// The _app paths

+ 1 - 1
MediaBrowser.Api/LocalizationService.cs

@@ -40,7 +40,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class CulturesService
     /// </summary>
-    public class LocalizationService : BaseRestService
+    public class LocalizationService : BaseApiService
     {
         /// <summary>
         /// Gets the specified request.

+ 1 - 0
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -81,6 +81,7 @@
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
+    <Compile Include="BaseApiService.cs" />
     <Compile Include="EnvironmentService.cs" />
     <Compile Include="Images\ImageRequest.cs" />
     <Compile Include="Images\ImageService.cs" />

+ 1 - 1
MediaBrowser.Api/PackageService.cs

@@ -104,7 +104,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class PackageService
     /// </summary>
-    public class PackageService : BaseRestService
+    public class PackageService : BaseApiService
     {
         private readonly IInstallationManager _installationManager;
         private readonly IApplicationHost _appHost;

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -21,7 +21,7 @@ namespace MediaBrowser.Api.Playback
     /// <summary>
     /// Class BaseStreamingService
     /// </summary>
-    public abstract class BaseStreamingService : BaseRestService
+    public abstract class BaseStreamingService : BaseApiService
     {
         /// <summary>
         /// Gets or sets the application paths.

+ 1 - 1
MediaBrowser.Api/PluginService.cs

@@ -126,7 +126,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class PluginsService
     /// </summary>
-    public class PluginService : BaseRestService
+    public class PluginService : BaseApiService
     {
         /// <summary>
         /// The _json serializer

+ 1 - 1
MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs

@@ -83,7 +83,7 @@ namespace MediaBrowser.Api.ScheduledTasks
     /// <summary>
     /// Class ScheduledTasksService
     /// </summary>
-    public class ScheduledTaskService : BaseRestService
+    public class ScheduledTaskService : BaseApiService
     {
         /// <summary>
         /// Gets or sets the task manager.

+ 1 - 2
MediaBrowser.Api/SystemService.cs

@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.System;
-using MediaBrowser.Server.Implementations.HttpServer;
 using ServiceStack.ServiceHost;
 using System;
 using System.IO;
@@ -59,7 +58,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class SystemInfoService
     /// </summary>
-    public class SystemService : BaseRestService
+    public class SystemService : BaseApiService
     {
         /// <summary>
         /// The _json serializer

+ 1 - 1
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -16,7 +16,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// Class BaseItemsByNameService
     /// </summary>
     /// <typeparam name="TItemType">The type of the T item type.</typeparam>
-    public abstract class BaseItemsByNameService<TItemType> : BaseRestService
+    public abstract class BaseItemsByNameService<TItemType> : BaseApiService
         where TItemType : BaseItem
     {
         /// <summary>

+ 1 - 1
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -112,7 +112,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// Class ItemsService
     /// </summary>
-    public class ItemsService : BaseRestService
+    public class ItemsService : BaseApiService
     {
         /// <summary>
         /// The _user manager

+ 34 - 6
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -6,6 +6,7 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Server.Implementations.HttpServer;
+using MediaBrowser.Server.Implementations.Library;
 using ServiceStack.ServiceHost;
 using ServiceStack.Text.Controller;
 using System;
@@ -347,7 +348,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// Class UserLibraryService
     /// </summary>
-    public class UserLibraryService : BaseRestService
+    public class UserLibraryService : BaseApiService
     {
         /// <summary>
         /// The _user manager
@@ -592,7 +593,16 @@ namespace MediaBrowser.Api.UserLibrary
 
             var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
 
-            _userManager.OnPlaybackStart(user, item, ClientType.Other, string.Empty);
+            var auth = RequestFilterAttribute.GetAuthorization(Request);
+
+            if (auth != null)
+            {
+                ClientType clientType;
+
+                Enum.TryParse(auth["Client"] ?? string.Empty, out clientType);
+
+                _userManager.OnPlaybackStart(user, item, clientType, auth["DeviceId"], auth["Device"] ?? string.Empty);
+            }
         }
 
         /// <summary>
@@ -605,9 +615,18 @@ namespace MediaBrowser.Api.UserLibrary
 
             var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
 
-            var task = _userManager.OnPlaybackProgress(user, item, request.PositionTicks, ClientType.Other, string.Empty);
+            var auth = RequestFilterAttribute.GetAuthorization(Request);
 
-            Task.WaitAll(task);
+            if (auth != null)
+            {
+                ClientType clientType;
+
+                Enum.TryParse(auth["Client"] ?? string.Empty, out clientType);
+
+                var task = _userManager.OnPlaybackProgress(user, item, request.PositionTicks, clientType, auth["DeviceId"], auth["Device"] ?? string.Empty);
+
+                Task.WaitAll(task);
+            }
         }
 
         /// <summary>
@@ -620,9 +639,18 @@ namespace MediaBrowser.Api.UserLibrary
 
             var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
 
-            var task = _userManager.OnPlaybackStopped(user, item, request.PositionTicks, ClientType.Other, string.Empty);
+            var auth = RequestFilterAttribute.GetAuthorization(Request);
 
-            Task.WaitAll(task);
+            if (auth != null)
+            {
+                ClientType clientType;
+
+                Enum.TryParse(auth["Client"] ?? string.Empty, out clientType);
+
+                var task = _userManager.OnPlaybackStopped(user, item, request.PositionTicks, clientType, auth["DeviceId"], auth["Device"] ?? string.Empty);
+
+                Task.WaitAll(task);
+            }
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Api/UserService.cs

@@ -126,7 +126,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class UsersService
     /// </summary>
-    public class UserService : BaseRestService
+    public class UserService : BaseApiService
     {
         /// <summary>
         /// The _XML serializer

+ 1 - 1
MediaBrowser.Api/WeatherService.cs

@@ -25,7 +25,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class WeatherService
     /// </summary>
-    public class WeatherService : BaseRestService
+    public class WeatherService : BaseApiService
     {
         /// <summary>
         /// Gets the specified request.

+ 8 - 4
MediaBrowser.Controller/Library/IUserManager.cs

@@ -69,10 +69,11 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="user">The user.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">user</exception>
-        Task LogUserActivity(User user, ClientType clientType, string deviceName);
+        Task LogUserActivity(User user, ClientType clientType, string deviceId, string deviceName);
 
         /// <summary>
         /// Refreshes metadata for each user
@@ -124,9 +125,10 @@ namespace MediaBrowser.Controller.Library
         /// <param name="user">The user.</param>
         /// <param name="item">The item.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <exception cref="System.ArgumentNullException"></exception>
-        void OnPlaybackStart(User user, BaseItem item, ClientType clientType, string deviceName);
+        void OnPlaybackStart(User user, BaseItem item, ClientType clientType, string deviceId, string deviceName);
 
         /// <summary>
         /// Used to report playback progress for an item
@@ -135,10 +137,11 @@ namespace MediaBrowser.Controller.Library
         /// <param name="item">The item.</param>
         /// <param name="positionTicks">The position ticks.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceName);
+        Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceId, string deviceName);
 
         /// <summary>
         /// Used to report that playback has ended for an item
@@ -147,10 +150,11 @@ namespace MediaBrowser.Controller.Library
         /// <param name="item">The item.</param>
         /// <param name="positionTicks">The position ticks.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceName);
+        Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceId, string deviceName);
 
         /// <summary>
         /// Saves user data for an item

+ 7 - 0
MediaBrowser.Model/Connectivity/ClientConnectionInfo.cs

@@ -51,5 +51,12 @@ namespace MediaBrowser.Model.Connectivity
         /// <value>The now playing position ticks.</value>
         [ProtoMember(6)]
         public long? NowPlayingPositionTicks { get; set; }
+
+        /// <summary>
+        /// Gets or sets the device id.
+        /// </summary>
+        /// <value>The device id.</value>
+        [ProtoMember(7)]
+        public string DeviceId { get; set; }
     }
 }

+ 3 - 0
MediaBrowser.Model/Connectivity/ClientType.cs

@@ -17,6 +17,9 @@
         /// The dashboard
         /// </summary>
         Dashboard,
+        /// <summary>
+        /// The dlna
+        /// </summary>
         Dlna,
         /// <summary>
         /// The ios

+ 6 - 0
MediaBrowser.Model/Querying/ItemQuery.cs

@@ -122,5 +122,11 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         /// <value>The image types.</value>
         public ImageType[] ImageTypes { get; set; }
+
+        /// <summary>
+        /// Gets or sets the ids, which are specific items to retrieve
+        /// </summary>
+        /// <value>The ids.</value>
+        public string[] Ids { get; set; }
     }
 }

+ 30 - 20
MediaBrowser.Server.Implementations/Library/UserManager.cs

@@ -62,7 +62,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 }
             }
         }
-        
+
         /// <summary>
         /// Gets all connections.
         /// </summary>
@@ -71,7 +71,7 @@ namespace MediaBrowser.Server.Implementations.Library
         {
             get { return _activeConnections.Where(c => GetUserById(c.UserId) != null).OrderByDescending(c => c.LastActivityDate); }
         }
-        
+
         /// <summary>
         /// Gets the active connections.
         /// </summary>
@@ -172,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             return Users.FirstOrDefault(u => u.Id == id);
         }
-        
+
         /// <summary>
         /// Authenticates a User and returns a result indicating whether or not it succeeded
         /// </summary>
@@ -222,10 +222,11 @@ namespace MediaBrowser.Server.Implementations.Library
         /// </summary>
         /// <param name="user">The user.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">user</exception>
-        public Task LogUserActivity(User user, ClientType clientType, string deviceName)
+        public Task LogUserActivity(User user, ClientType clientType, string deviceId, string deviceName)
         {
             if (user == null)
             {
@@ -236,7 +237,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             user.LastActivityDate = activityDate;
 
-            LogConnection(user.Id, clientType, deviceName, activityDate);
+            LogConnection(user.Id, clientType, deviceId, deviceName, activityDate);
 
             // Save this directly. No need to fire off all the events for this.
             return Kernel.UserRepository.SaveUser(user, CancellationToken.None);
@@ -247,15 +248,17 @@ namespace MediaBrowser.Server.Implementations.Library
         /// </summary>
         /// <param name="user">The user.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <param name="item">The item.</param>
         /// <param name="currentPositionTicks">The current position ticks.</param>
-        private void UpdateNowPlayingItemId(User user, ClientType clientType, string deviceName, BaseItem item, long? currentPositionTicks = null)
+        private void UpdateNowPlayingItemId(User user, ClientType clientType, string deviceId, string deviceName, BaseItem item, long? currentPositionTicks = null)
         {
-            var conn = GetConnection(user.Id, clientType, deviceName);
+            var conn = GetConnection(user.Id, clientType, deviceId, deviceName);
 
             conn.NowPlayingPositionTicks = currentPositionTicks;
             conn.NowPlayingItem = DtoBuilder.GetBaseItemInfo(item);
+            conn.LastActivityDate = DateTime.UtcNow;
         }
 
         /// <summary>
@@ -263,11 +266,12 @@ namespace MediaBrowser.Server.Implementations.Library
         /// </summary>
         /// <param name="user">The user.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <param name="item">The item.</param>
-        private void RemoveNowPlayingItemId(User user, ClientType clientType, string deviceName, BaseItem item)
+        private void RemoveNowPlayingItemId(User user, ClientType clientType, string deviceId, string deviceName, BaseItem item)
         {
-            var conn = GetConnection(user.Id, clientType, deviceName);
+            var conn = GetConnection(user.Id, clientType, deviceId, deviceName);
 
             if (conn.NowPlayingItem != null && conn.NowPlayingItem.Id.Equals(item.Id.ToString()))
             {
@@ -281,11 +285,12 @@ namespace MediaBrowser.Server.Implementations.Library
         /// </summary>
         /// <param name="userId">The user id.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <param name="lastActivityDate">The last activity date.</param>
-        private void LogConnection(Guid userId, ClientType clientType, string deviceName, DateTime lastActivityDate)
+        private void LogConnection(Guid userId, ClientType clientType, string deviceId, string deviceName, DateTime lastActivityDate)
         {
-            GetConnection(userId, clientType, deviceName).LastActivityDate = lastActivityDate;
+            GetConnection(userId, clientType, deviceId, deviceName).LastActivityDate = lastActivityDate;
         }
 
         /// <summary>
@@ -293,11 +298,12 @@ namespace MediaBrowser.Server.Implementations.Library
         /// </summary>
         /// <param name="userId">The user id.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>ClientConnectionInfo.</returns>
-        private ClientConnectionInfo GetConnection(Guid userId, ClientType clientType, string deviceName)
+        private ClientConnectionInfo GetConnection(Guid userId, ClientType clientType, string deviceId, string deviceName)
         {
-            var conn = _activeConnections.FirstOrDefault(c => c.UserId == userId && c.ClientType == clientType && string.Equals(deviceName, c.DeviceName, StringComparison.OrdinalIgnoreCase));
+            var conn = _activeConnections.FirstOrDefault(c => c.UserId == userId && c.ClientType == clientType && string.Equals(deviceId, c.DeviceId));
 
             if (conn == null)
             {
@@ -305,7 +311,8 @@ namespace MediaBrowser.Server.Implementations.Library
                 {
                     UserId = userId,
                     ClientType = clientType,
-                    DeviceName = deviceName
+                    DeviceName = deviceName,
+                    DeviceId = deviceId
                 };
 
                 _activeConnections.Add(conn);
@@ -524,9 +531,10 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="user">The user.</param>
         /// <param name="item">The item.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public void OnPlaybackStart(User user, BaseItem item, ClientType clientType, string deviceName)
+        public void OnPlaybackStart(User user, BaseItem item, ClientType clientType, string deviceId, string deviceName)
         {
             if (user == null)
             {
@@ -537,7 +545,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException();
             }
 
-            UpdateNowPlayingItemId(user, clientType, deviceName, item);
+            UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item);
 
             // Nothing to save here
             // Fire events to inform plugins
@@ -555,10 +563,11 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="item">The item.</param>
         /// <param name="positionTicks">The position ticks.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public async Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceName)
+        public async Task OnPlaybackProgress(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceId, string deviceName)
         {
             if (user == null)
             {
@@ -569,7 +578,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException();
             }
 
-            UpdateNowPlayingItemId(user, clientType, deviceName, item, positionTicks);
+            UpdateNowPlayingItemId(user, clientType, deviceId, deviceName, item, positionTicks);
 
             if (positionTicks.HasValue)
             {
@@ -594,10 +603,11 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="item">The item.</param>
         /// <param name="positionTicks">The position ticks.</param>
         /// <param name="clientType">Type of the client.</param>
+        /// <param name="deviceId">The device id.</param>
         /// <param name="deviceName">Name of the device.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public async Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceName)
+        public async Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, ClientType clientType, string deviceId, string deviceName)
         {
             if (user == null)
             {
@@ -608,7 +618,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException();
             }
 
-            RemoveNowPlayingItemId(user, clientType, deviceName, item);
+            RemoveNowPlayingItemId(user, clientType, deviceId, deviceName, item);
 
             var data = item.GetUserData(user, true);
 

+ 18 - 7
MediaBrowser.WebDashboard/ApiClient.js

@@ -13,6 +13,8 @@ var ApiClient = {
 
     serverPortNumber: 8096,
 
+    currentUserId: null,
+
     /**
      * Detects the hostname and port of MB server based on the current url
      */
@@ -36,14 +38,12 @@ var ApiClient = {
             throw new Error("Url name cannot be empty");
         }
 
-        params = params || {};
-
         var url = ApiClient.serverProtocol + "//" + ApiClient.serverHostName + ":" + ApiClient.serverPortNumber + "/mediabrowser/" + name;
 
         if (params) {
             url += "?" + $.param(params);
-
         }
+
         return url;
     },
 
@@ -76,6 +76,10 @@ var ApiClient = {
 
         return "Web Browser";
     },
+    
+    getDeviceId: function() {
+        return SHA1(navigator.userAgent + (navigator.cpuClass || ""));
+    },
 
     /**
      * Creates a custom api url based on a handler name and query string parameters
@@ -89,16 +93,14 @@ var ApiClient = {
         }
 
         params = params || {};
-        params.client = "Dashboard";
-        params.device = ApiClient.getDeviceName();
         params.format = "json";
 
         var url = ApiClient.serverProtocol + "//" + ApiClient.serverHostName + ":" + ApiClient.serverPortNumber + "/mediabrowser/" + name;
 
         if (params) {
             url += "?" + $.param(params);
-
         }
+
         return url;
     },
 
@@ -1063,7 +1065,7 @@ var ApiClient = {
         var postData = {
             password: SHA1(password || "")
         };
-        
+
         return $.ajax({
             type: "POST",
             url: url,
@@ -1380,3 +1382,12 @@ var ApiClient = {
 
 // Do this initially. The consumer can always override later
 ApiClient.inferServerFromUrl();
+
+$(document).ajaxSend(function (event, jqXHR) {
+
+    if (ApiClient.currentUserId) {
+
+        var auth = 'MediaBrowser UserId="' + ApiClient.currentUserId + '", Client="Dashboard", Device="' + ApiClient.getDeviceName() + '", DeviceId="' + ApiClient.getDeviceName() + '"';
+        jqXHR.setRequestHeader("Authorization", auth);
+    }
+});

+ 13 - 2
MediaBrowser.WebDashboard/Html/scripts/site.js

@@ -81,12 +81,14 @@ var Dashboard = {
 
     setCurrentUser: function (userId) {
         localStorage.setItem("userId", userId);
+        ApiClient.currentUserId = userId;
         Dashboard.getUserPromise = null;
     },
 
     logout: function () {
         localStorage.removeItem("userId");
         Dashboard.getUserPromise = null;
+        ApiClient.currentUserId = null;
         window.location = "login.html";
     },
 
@@ -1113,6 +1115,13 @@ var Dashboard = {
                     tag: item.PrimaryImageTag,
                     type: "Primary"
                 });
+                
+                if (!item.Id || data.icon.indexOf("undefined") != -1) {
+                    alert("bad image url: " + JSON.stringify(item));
+                    console.log("bad image url: " + JSON.stringify(item));
+
+                    continue;
+                }
             }
 
             WebNotifications.show(data);
@@ -1200,9 +1209,11 @@ $(document).on('pagebeforeshow', ".page", function () {
 }).on('pageinit', ".page", function () {
 
     var page = $(this);
-    var hasLogin = Dashboard.getCurrentUserId();
+    
+    var userId = Dashboard.getCurrentUserId();
+    ApiClient.currentUserId = userId;
 
-    if (!hasLogin) {
+    if (!userId) {
 
         if (this.id !== "loginPage" && !page.hasClass('wizardPage')) {
 

+ 3 - 0
MediaBrowser.sln

@@ -221,4 +221,7 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 EndGlobal

+ 2 - 2
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.47</version>
+        <version>3.0.48</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 Theatre 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.47" />
+            <dependency id="MediaBrowser.Common" version="3.0.48" />
             <dependency id="NLog" version="2.0.0.2000" />
             <dependency id="ServiceStack.Text" version="3.9.38" />
             <dependency id="protobuf-net" version="2.0.0.621" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.47</version>
+        <version>3.0.48</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
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.47</version>
+        <version>3.0.48</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.47" />
+            <dependency id="MediaBrowser.Common" version="3.0.48" />
         </dependencies>
     </metadata>
     <files>