diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index c84239f92a..99ce17ebdd 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -162,17 +162,17 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager using (var message = GetHttpRequestMessage(options)) { - if (options.EnableResponseCache && cachedInfo != null) - { - if (!string.IsNullOrEmpty(cachedInfo.Etag)) - { - message.Headers.Add("If-None-Match", cachedInfo.Etag); - } - else if (cachedInfo.LastModified.HasValue) - { - message.Headers.IfModifiedSince = new DateTimeOffset(cachedInfo.LastModified.Value); - } - } + //if (options.EnableResponseCache && cachedInfo != null) + //{ + // if (!string.IsNullOrEmpty(cachedInfo.Etag)) + // { + // message.Headers.Add("If-None-Match", cachedInfo.Etag); + // } + // else if (cachedInfo.LastModified.HasValue) + // { + // message.Headers.IfModifiedSince = new DateTimeOffset(cachedInfo.LastModified.Value); + // } + //} if (options.ResourcePool != null) { diff --git a/MediaBrowser.Common/Net/IServerManager.cs b/MediaBrowser.Common/Net/IServerManager.cs index 0f95c775ed..3234e70600 100644 --- a/MediaBrowser.Common/Net/IServerManager.cs +++ b/MediaBrowser.Common/Net/IServerManager.cs @@ -52,10 +52,27 @@ namespace MediaBrowser.Common.Net /// messageType Task SendWebSocketMessageAsync(string messageType, Func dataFunction, CancellationToken cancellationToken); + /// + /// Sends the web socket message async. + /// + /// + /// Type of the message. + /// The data function. + /// The connections. + /// The cancellation token. + /// Task. + Task SendWebSocketMessageAsync(string messageType, Func dataFunction, IEnumerable connections, CancellationToken cancellationToken); + /// /// Adds the web socket listeners. /// /// The listeners. void AddWebSocketListeners(IEnumerable listeners); + + /// + /// Gets the web socket connections. + /// + /// The web socket connections. + IEnumerable WebSocketConnections { get; } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs index 4837b4da9b..588f6db4fc 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs @@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.Providers.Music private string FindIdFromMusicArtistEntity(BaseItem item) { var artist = _libraryManager.RootFolder.RecursiveChildren.OfType() - .FirstOrDefault(i => string.Equals(i.Name, item.Name, StringComparison.OrdinalIgnoreCase)); + .FirstOrDefault(i => string.Compare(i.Name, item.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0); return artist != null ? artist.GetProviderId(MetadataProviders.Musicbrainz) : null; } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 2068ac0da6..72cf42c14c 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Library public class LibraryManager : ILibraryManager { private IEnumerable PrescanTasks { get; set; } - + /// /// Gets the intro providers. /// @@ -306,7 +306,20 @@ namespace MediaBrowser.Server.Implementations.Library /// BaseItem. public BaseItem ResolveItem(ItemResolveArgs args) { - var item = EntityResolvers.Select(r => r.ResolvePath(args)).FirstOrDefault(i => i != null); + var item = EntityResolvers.Select(r => + { + try + { + return r.ResolvePath(args); + } + catch (Exception ex) + { + _logger.ErrorException("Error in {0} resolving {1}", ex, r.GetType().Name, args.Path); + + return null; + } + + }).FirstOrDefault(i => i != null); if (item != null) { @@ -1028,7 +1041,7 @@ namespace MediaBrowser.Server.Implementations.Library await SaveItem(item, cancellationToken).ConfigureAwait(false); UpdateItemInLibraryCache(item); - + if (ItemAdded != null) { try diff --git a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs index a45804f69c..f8e47434e2 100644 --- a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs +++ b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs @@ -36,6 +36,14 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// The web socket connections /// private readonly List _webSocketConnections = new List(); + /// + /// Gets the web socket connections. + /// + /// The web socket connections. + public IEnumerable WebSocketConnections + { + get { return _webSocketConnections; } + } /// /// Gets or sets the external web socket server. @@ -83,6 +91,9 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// The web socket listeners. private readonly List _webSocketListeners = new List(); + /// + /// The _kernel + /// private readonly Kernel _kernel; /// @@ -240,7 +251,26 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// The cancellation token. /// Task. /// messageType - public async Task SendWebSocketMessageAsync(string messageType, Func dataFunction, CancellationToken cancellationToken) + public Task SendWebSocketMessageAsync(string messageType, Func dataFunction, CancellationToken cancellationToken) + { + return SendWebSocketMessageAsync(messageType, dataFunction, _webSocketConnections, cancellationToken); + } + + /// + /// Sends the web socket message async. + /// + /// + /// Type of the message. + /// The data function. + /// The connections. + /// The cancellation token. + /// Task. + /// messageType + /// or + /// dataFunction + /// or + /// cancellationToken + public async Task SendWebSocketMessageAsync(string messageType, Func dataFunction, IEnumerable connections, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(messageType)) { @@ -259,16 +289,16 @@ namespace MediaBrowser.Server.Implementations.ServerManager cancellationToken.ThrowIfCancellationRequested(); - var connections = _webSocketConnections.Where(s => s.State == WebSocketState.Open).ToList(); + var connectionsList = connections.Where(s => s.State == WebSocketState.Open).ToList(); - if (connections.Count > 0) + if (connectionsList.Count > 0) { _logger.Info("Sending web socket message {0}", messageType); var message = new WebSocketMessage { MessageType = messageType, Data = dataFunction() }; var bytes = _jsonSerializer.SerializeToBytes(message); - var tasks = connections.Select(s => Task.Run(() => + var tasks = connectionsList.Select(s => Task.Run(() => { try { diff --git a/MediaBrowser.ServerApplication/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.ServerApplication/EntryPoints/LibraryChangedNotifier.cs index 62c1e17f90..715492aac4 100644 --- a/MediaBrowser.ServerApplication/EntryPoints/LibraryChangedNotifier.cs +++ b/MediaBrowser.ServerApplication/EntryPoints/LibraryChangedNotifier.cs @@ -1,8 +1,12 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; +using MoreLinq; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading; @@ -17,17 +21,19 @@ namespace MediaBrowser.ServerApplication.EntryPoints private readonly ISessionManager _sessionManager; private readonly IServerManager _serverManager; - + private readonly IUserManager _userManager; + /// /// The _library changed sync lock /// private readonly object _libraryChangedSyncLock = new object(); - /// - /// Gets or sets the library update info. - /// - /// The library update info. - private LibraryUpdateInfo LibraryUpdateInfo { get; set; } + private readonly List _foldersAddedTo = new List(); + private readonly List _foldersRemovedFrom = new List(); + + private readonly List _itemsAdded = new List(); + private readonly List _itemsRemoved = new List(); + private readonly List _itemsUpdated = new List(); /// /// Gets or sets the library update timer. @@ -40,11 +46,12 @@ namespace MediaBrowser.ServerApplication.EntryPoints /// private const int LibraryUpdateDuration = 60000; - public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IServerManager serverManager) + public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IServerManager serverManager, IUserManager userManager) { _libraryManager = libraryManager; _sessionManager = sessionManager; _serverManager = serverManager; + _userManager = userManager; } public void Run() @@ -64,11 +71,6 @@ namespace MediaBrowser.ServerApplication.EntryPoints { lock (_libraryChangedSyncLock) { - if (LibraryUpdateInfo == null) - { - LibraryUpdateInfo = new LibraryUpdateInfo(); - } - if (LibraryUpdateTimer == null) { LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, @@ -81,10 +83,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints if (e.Item.Parent != null) { - LibraryUpdateInfo.FoldersAddedTo.Add(e.Item.Parent.Id); + _foldersAddedTo.Add(e.Item.Parent); } - LibraryUpdateInfo.ItemsAdded.Add(e.Item.Id); + _itemsAdded.Add(e.Item); } } @@ -97,11 +99,6 @@ namespace MediaBrowser.ServerApplication.EntryPoints { lock (_libraryChangedSyncLock) { - if (LibraryUpdateInfo == null) - { - LibraryUpdateInfo = new LibraryUpdateInfo(); - } - if (LibraryUpdateTimer == null) { LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, @@ -112,7 +109,7 @@ namespace MediaBrowser.ServerApplication.EntryPoints LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite); } - LibraryUpdateInfo.ItemsUpdated.Add(e.Item.Id); + _itemsUpdated.Add(e.Item); } } @@ -125,11 +122,6 @@ namespace MediaBrowser.ServerApplication.EntryPoints { lock (_libraryChangedSyncLock) { - if (LibraryUpdateInfo == null) - { - LibraryUpdateInfo = new LibraryUpdateInfo(); - } - if (LibraryUpdateTimer == null) { LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, @@ -142,10 +134,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints if (e.Item.Parent != null) { - LibraryUpdateInfo.FoldersRemovedFrom.Add(e.Item.Parent.Id); + _foldersRemovedFrom.Add(e.Item.Parent); } - LibraryUpdateInfo.ItemsRemoved.Add(e.Item.Id); + _itemsRemoved.Add(e.Item); } } @@ -158,16 +150,16 @@ namespace MediaBrowser.ServerApplication.EntryPoints lock (_libraryChangedSyncLock) { // Remove dupes in case some were saved multiple times - LibraryUpdateInfo.FoldersAddedTo = LibraryUpdateInfo.FoldersAddedTo.Distinct().ToList(); + var foldersAddedTo = _foldersAddedTo.DistinctBy(i => i.Id).ToList(); - LibraryUpdateInfo.FoldersRemovedFrom = LibraryUpdateInfo.FoldersRemovedFrom.Distinct().ToList(); + var foldersRemovedFrom = _foldersRemovedFrom.DistinctBy(i => i.Id).ToList(); - LibraryUpdateInfo.ItemsUpdated = LibraryUpdateInfo.ItemsUpdated - .Where(i => !LibraryUpdateInfo.ItemsAdded.Contains(i)) - .Distinct() + var itemsUpdated = _itemsUpdated + .Where(i => !_itemsAdded.Contains(i)) + .DistinctBy(i => i.Id) .ToList(); - _serverManager.SendWebSocketMessage("LibraryChanged", LibraryUpdateInfo); + SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None); if (LibraryUpdateTimer != null) { @@ -175,8 +167,112 @@ namespace MediaBrowser.ServerApplication.EntryPoints LibraryUpdateTimer = null; } - LibraryUpdateInfo = null; + _itemsAdded.Clear(); + _itemsRemoved.Clear(); + _itemsUpdated.Clear(); + _foldersAddedTo.Clear(); + _foldersRemovedFrom.Clear(); + } + } + + /// + /// Sends the change notifications. + /// + /// The items added. + /// The items updated. + /// The items removed. + /// The folders added to. + /// The folders removed from. + /// The cancellation token. + private async void SendChangeNotifications(IEnumerable itemsAdded, IEnumerable itemsUpdated, IEnumerable itemsRemoved, IEnumerable foldersAddedTo, IEnumerable foldersRemovedFrom, CancellationToken cancellationToken) + { + var currentSessions = _sessionManager.Sessions.ToList(); + + var users = currentSessions.Select(i => i.UserId ?? Guid.Empty).Where(i => i != Guid.Empty).Distinct().ToList(); + + foreach (var userId in users) + { + var id = userId; + var webSockets = currentSessions.Where(u => u.UserId.HasValue && u.UserId.Value == id).SelectMany(i => i.WebSockets).ToList(); + + await _serverManager.SendWebSocketMessageAsync("LibraryChanged", () => GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, id), webSockets, cancellationToken).ConfigureAwait(false); + } + } + + /// + /// Gets the library update info. + /// + /// The items added. + /// The items updated. + /// The items removed. + /// The folders added to. + /// The folders removed from. + /// The user id. + /// LibraryUpdateInfo. + private LibraryUpdateInfo GetLibraryUpdateInfo(IEnumerable itemsAdded, IEnumerable itemsUpdated, IEnumerable itemsRemoved, IEnumerable foldersAddedTo, IEnumerable foldersRemovedFrom, Guid userId) + { + var user = _userManager.GetUserById(userId); + + var collections = user.RootFolder.GetChildren(user).ToList(); + + var allRecursiveChildren = user.RootFolder.GetRecursiveChildren(user).ToDictionary(i => i.Id); + + return new LibraryUpdateInfo + { + ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, collections, allRecursiveChildren)).Select(i => i.Id).Distinct().ToList(), + + ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, collections, allRecursiveChildren)).Select(i => i.Id).Distinct().ToList(), + + ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, collections, allRecursiveChildren)).Select(i => i.Id).Distinct().ToList(), + + FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, collections, allRecursiveChildren)).Select(i => i.Id).Distinct().ToList(), + + FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, collections, allRecursiveChildren)).Select(i => i.Id).Distinct().ToList() + }; + } + + /// + /// Translates the physical item to user library. + /// + /// + /// The item. + /// The user. + /// The collections. + /// All recursive children. + /// IEnumerable{``0}. + private IEnumerable TranslatePhysicalItemToUserLibrary(T item, User user, List collections, Dictionary allRecursiveChildren) + where T : BaseItem + { + // If the physical root changed, return the user root + if (item is AggregateFolder) + { + return new T[] { user.RootFolder as T }; + } + + // Need to find what user collection folder this belongs to + if (item.Parent is AggregateFolder) + { + return new T[] { user.RootFolder as T }; + } + + // If it's a user root, return it only if it's the right one + if (item is UserRootFolder) + { + if (item.Id == user.RootFolder.Id) + { + return new T[] { item }; + } + + return new T[] { }; } + + // Return it only if it's in the user's library + if (allRecursiveChildren.ContainsKey(item.Id)) + { + return new T[] { item }; + } + + return new T[] { }; } /// diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index a624f8e421..af0fa257d9 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -130,6 +130,10 @@ False ..\packages\MediaBrowser.IsoMounting.3.0.51\lib\net45\MediaBrowser.IsoMounter.dll + + False + ..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll + False ..\packages\NLog.2.0.1.2\lib\net45\NLog.dll diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index b411ab00ef..1679a99faf 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -4,6 +4,7 @@ +