diff options
| author | Eric Reed <ebr@mediabrowser3.com> | 2013-05-22 09:09:44 -0400 |
|---|---|---|
| committer | Eric Reed <ebr@mediabrowser3.com> | 2013-05-22 09:09:44 -0400 |
| commit | 07802a5b6d9abf7f67376e05c5bdaa96a0ac9337 (patch) | |
| tree | 02b7c251fd5f3e0b2b1f3fb4995ca5c0a1ceda3b | |
| parent | 212e696f7e76c37b8134acf87f7354baabcb63d4 (diff) | |
| parent | 07b7ab9a0b64d51646addc9a2a75189c2981a854 (diff) | |
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
24 files changed, 362 insertions, 174 deletions
diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 87bf08e63..f1e9e6f9a 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -96,6 +96,20 @@ namespace MediaBrowser.Api { } + [Route("/Items/{Id}/Refresh", "POST")] + [Api(Description = "Refreshes metadata for an item")] + public class RefreshItem : IReturnVoid + { + [ApiMember(Name = "Forced", Description = "Indicates if a normal or forced refresh should occur.", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool Forced { get; set; } + + [ApiMember(Name = "Recursive", Description = "Indicates if the refresh should occur recursively.", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool Recursive { get; set; } + + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + } + [Route("/Items/Counts", "GET")] [Api(Description = "Gets counts of various item types")] public class GetItemCounts : IReturn<ItemCounts> @@ -305,6 +319,26 @@ namespace MediaBrowser.Api } /// <summary> + /// Posts the specified request. + /// </summary> + /// <param name="request">The request.</param> + public void Post(RefreshItem request) + { + var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager); + + var folder = item as Folder; + + if (folder != null) + { + folder.ValidateChildren(new Progress<double>(), CancellationToken.None, request.Recursive, request.Forced); + } + else + { + item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced); + } + } + + /// <summary> /// Gets the specified request. /// </summary> /// <param name="request">The request.</param> @@ -467,7 +501,7 @@ namespace MediaBrowser.Api { var artists1 = item1.RecursiveChildren .OfType<Audio>() - .SelectMany(i => new[]{i.AlbumArtist, i.Artist}) + .SelectMany(i => new[] { i.AlbumArtist, i.Artist }) .Where(i => !string.IsNullOrEmpty(i)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b2b37a1ac..c6a7470b7 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -426,11 +426,6 @@ namespace MediaBrowser.Api.Playback { if (audioStream.Channels > 2 && request.AudioCodec.HasValue) { - if (request.AudioCodec.Value == AudioCodecs.Aac) - { - // libvo_aacenc currently only supports two channel output - return 2; - } if (request.AudioCodec.Value == AudioCodecs.Wma) { // wmav2 currently only supports two channel output @@ -465,7 +460,7 @@ namespace MediaBrowser.Api.Playback { if (codec == AudioCodecs.Aac) { - return "libvo_aacenc"; + return "aac"; } if (codec == AudioCodecs.Mp3) { diff --git a/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs b/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs index f72006a23..a666d4628 100644 --- a/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs @@ -93,6 +93,11 @@ namespace MediaBrowser.Api.Playback.Hls var args = "-codec:a " + codec; + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) + { + args += " -strict experimental"; + } + var channels = GetNumAudioChannelsParam(state.Request, state.AudioStream); if (channels.HasValue) diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 42a1bf839..827333598 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -98,6 +98,11 @@ namespace MediaBrowser.Api.Playback.Hls if (state.AudioStream != null) { + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) + { + args += " -strict experimental"; + } + var channels = GetNumAudioChannelsParam(state.Request, state.AudioStream); if (channels.HasValue) diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index f5a95d898..b6a7c7204 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -3,7 +3,9 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using ServiceStack.ServiceHost; +using System; using System.Collections.Generic; +using System.IO; namespace MediaBrowser.Api.Playback.Progressive { @@ -84,6 +86,11 @@ namespace MediaBrowser.Api.Playback.Progressive var audioTranscodeParams = new List<string>(); + if (string.Equals(Path.GetExtension(outputPath), ".aac", StringComparison.OrdinalIgnoreCase)) + { + audioTranscodeParams.Add("-strict experimental"); + } + if (request.AudioBitRate.HasValue) { audioTranscodeParams.Add("-ab " + request.AudioBitRate.Value); diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 117b6033f..3e92c96ec 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -217,6 +217,11 @@ namespace MediaBrowser.Api.Playback.Progressive var args = "-acodec " + codec; + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) + { + args += " -strict experimental"; + } + // Add the number of audio channels var channels = GetNumAudioChannelsParam(request, state.AudioStream); diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index c84239f92..d01c9e205 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -160,90 +160,89 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - 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); - } - } + 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.ResourcePool != null) - { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } + if (options.ResourcePool != null) + { + await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); + } - _logger.Info("HttpClientManager.Get url: {0}", options.Url); + _logger.Info("HttpClientManager.Get url: {0}", options.Url); - try - { - options.CancellationToken.ThrowIfCancellationRequested(); + try + { + options.CancellationToken.ThrowIfCancellationRequested(); - var response = await GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression).SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); + var response = await GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression).SendAsync(message, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false); - if (options.EnableResponseCache) + if (options.EnableResponseCache) + { + if (response.StatusCode != HttpStatusCode.NotModified) { - if (response.StatusCode != HttpStatusCode.NotModified) - { - EnsureSuccessStatusCode(response); - } - - options.CancellationToken.ThrowIfCancellationRequested(); - - cachedInfo = UpdateInfoCache(cachedInfo, options.Url, cachedInfoPath, response); + EnsureSuccessStatusCode(response); + } - if (response.StatusCode == HttpStatusCode.NotModified) - { - _logger.Debug("Server indicates not modified for {0}. Returning cached result.", options.Url); + options.CancellationToken.ThrowIfCancellationRequested(); - return GetCachedResponse(cachedReponsePath); - } + cachedInfo = UpdateInfoCache(cachedInfo, options.Url, cachedInfoPath, response); - if (!string.IsNullOrEmpty(cachedInfo.Etag) || cachedInfo.LastModified.HasValue || - (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > DateTime.UtcNow)) - { - await UpdateResponseCache(response, cachedReponsePath).ConfigureAwait(false); + if (response.StatusCode == HttpStatusCode.NotModified) + { + _logger.Debug("Server indicates not modified for {0}. Returning cached result.", options.Url); - return GetCachedResponse(cachedReponsePath); - } + return GetCachedResponse(cachedReponsePath); } - else + + if (!string.IsNullOrEmpty(cachedInfo.Etag) || cachedInfo.LastModified.HasValue || + (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > DateTime.UtcNow)) { - EnsureSuccessStatusCode(response); + await UpdateResponseCache(response, cachedReponsePath).ConfigureAwait(false); - options.CancellationToken.ThrowIfCancellationRequested(); + return GetCachedResponse(cachedReponsePath); } - - return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); } - catch (OperationCanceledException ex) + else { - throw GetCancellationException(options.Url, options.CancellationToken, ex); - } - catch (HttpRequestException ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); + EnsureSuccessStatusCode(response); - throw new HttpException(ex.Message, ex); + options.CancellationToken.ThrowIfCancellationRequested(); } - catch (Exception ex) - { - _logger.ErrorException("Error getting response from " + options.Url, ex); - throw; - } - finally + return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + } + catch (OperationCanceledException ex) + { + throw GetCancellationException(options.Url, options.CancellationToken, ex); + } + catch (HttpRequestException ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw new HttpException(ex.Message, ex); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting response from " + options.Url, ex); + + throw; + } + finally + { + if (options.ResourcePool != null) { - if (options.ResourcePool != null) - { - options.ResourcePool.Release(); - } + options.ResourcePool.Release(); } } } diff --git a/MediaBrowser.Common/Net/IServerManager.cs b/MediaBrowser.Common/Net/IServerManager.cs index 0f95c775e..3234e7060 100644 --- a/MediaBrowser.Common/Net/IServerManager.cs +++ b/MediaBrowser.Common/Net/IServerManager.cs @@ -53,9 +53,26 @@ namespace MediaBrowser.Common.Net Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken); /// <summary> + /// Sends the web socket message async. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="messageType">Type of the message.</param> + /// <param name="dataFunction">The data function.</param> + /// <param name="connections">The connections.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> connections, CancellationToken cancellationToken); + + /// <summary> /// Adds the web socket listeners. /// </summary> /// <param name="listeners">The listeners.</param> void AddWebSocketListeners(IEnumerable<IWebSocketListener> listeners); + + /// <summary> + /// Gets the web socket connections. + /// </summary> + /// <value>The web socket connections.</value> + IEnumerable<IWebSocketConnection> WebSocketConnections { get; } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Dto/SessionInfoDtoBuilder.cs b/MediaBrowser.Controller/Dto/SessionInfoDtoBuilder.cs index 27a237ba7..ce8e510b8 100644 --- a/MediaBrowser.Controller/Dto/SessionInfoDtoBuilder.cs +++ b/MediaBrowser.Controller/Dto/SessionInfoDtoBuilder.cs @@ -26,7 +26,8 @@ namespace MediaBrowser.Controller.Dto SupportsRemoteControl = session.SupportsRemoteControl, IsPaused = session.IsPaused, NowViewingContext = session.NowViewingContext, - NowViewingItemIdentifier = session.NowViewingItemIdentifier, + NowViewingItemId = session.NowViewingItemId, + NowViewingItemName = session.NowViewingItemName, NowViewingItemType = session.NowViewingItemType }; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 70f3548f3..64e5da92e 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -55,8 +55,9 @@ namespace MediaBrowser.Controller.Entities /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param> /// <returns>Task.</returns> - protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null) + protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false) { //we don't directly validate our children //but we do need to clear out the index cache... diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 66a4ca215..16bfff564 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -554,8 +554,9 @@ namespace MediaBrowser.Controller.Entities /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param> /// <returns>Task.</returns> - public async Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null) + public async Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false) { cancellationToken.ThrowIfCancellationRequested(); @@ -575,7 +576,7 @@ namespace MediaBrowser.Controller.Entities var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(innerCancellationTokenSource.Token, cancellationToken); - await ValidateChildrenInternal(progress, linkedCancellationTokenSource.Token, recursive).ConfigureAwait(false); + await ValidateChildrenInternal(progress, linkedCancellationTokenSource.Token, recursive, forceRefreshMetadata).ConfigureAwait(false); } catch (OperationCanceledException ex) { @@ -606,8 +607,9 @@ namespace MediaBrowser.Controller.Entities /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param> /// <returns>Task.</returns> - protected async virtual Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null) + protected async virtual Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false) { // Nothing to do here if (LocationType != LocationType.FileSystem) @@ -723,7 +725,7 @@ namespace MediaBrowser.Controller.Entities cancellationToken.ThrowIfCancellationRequested(); - await RefreshChildren(validChildren, progress, cancellationToken, recursive).ConfigureAwait(false); + await RefreshChildren(validChildren, progress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false); progress.Report(100); } @@ -735,8 +737,9 @@ namespace MediaBrowser.Controller.Entities /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param> /// <returns>Task.</returns> - private async Task RefreshChildren(IEnumerable<Tuple<BaseItem, bool>> children, IProgress<double> progress, CancellationToken cancellationToken, bool? recursive) + private async Task RefreshChildren(IEnumerable<Tuple<BaseItem, bool>> children, IProgress<double> progress, CancellationToken cancellationToken, bool? recursive, bool forceRefreshMetadata = false) { var list = children.ToList(); @@ -760,7 +763,7 @@ namespace MediaBrowser.Controller.Entities var child = currentTuple.Item1; //refresh it - await child.RefreshMetadata(cancellationToken, resetResolveArgs: child.IsFolder, forceSave: currentTuple.Item2).ConfigureAwait(false); + await child.RefreshMetadata(cancellationToken, resetResolveArgs: child.IsFolder, forceSave: currentTuple.Item2, forceRefresh: forceRefreshMetadata).ConfigureAwait(false); // Refresh children if a folder and the item changed or recursive is set to true var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value)); diff --git a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs index 4837b4da9..588f6db4f 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<MusicArtist>() - .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.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index c9ed47756..c3651974b 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -63,7 +63,13 @@ namespace MediaBrowser.Controller.Session /// Gets or sets the now viewing item identifier. /// </summary> /// <value>The now viewing item identifier.</value> - public string NowViewingItemIdentifier { get; set; } + public string NowViewingItemId { get; set; } + + /// <summary> + /// Gets or sets the name of the now viewing item. + /// </summary> + /// <value>The name of the now viewing item.</value> + public string NowViewingItemName { get; set; } /// <summary> /// Gets or sets the now playing item. diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs index 2b9c1a64e..879d496b2 100644 --- a/MediaBrowser.Model/Session/SessionInfoDto.cs +++ b/MediaBrowser.Model/Session/SessionInfoDto.cs @@ -45,7 +45,13 @@ namespace MediaBrowser.Model.Session /// Gets or sets the now viewing item identifier. /// </summary> /// <value>The now viewing item identifier.</value> - public string NowViewingItemIdentifier { get; set; } + public string NowViewingItemId { get; set; } + + /// <summary> + /// Gets or sets the name of the now viewing item. + /// </summary> + /// <value>The name of the now viewing item.</value> + public string NowViewingItemName { get; set; } /// <summary> /// Gets or sets the name of the device. diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 2068ac0da..72cf42c14 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<ILibraryPrescanTask> PrescanTasks { get; set; } - + /// <summary> /// Gets the intro providers. /// </summary> @@ -306,7 +306,20 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>BaseItem.</returns> 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/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 2b85268cc..1069f6ef1 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -98,11 +97,6 @@ namespace MediaBrowser.Server.Implementations.Providers } /// <summary> - /// The _supported providers key - /// </summary> - private readonly Guid _supportedProvidersKey = "SupportedProviders".GetMD5(); - - /// <summary> /// Runs all metadata providers for an entity, and returns true or false indicating if at least one was refreshed and requires persistence /// </summary> /// <param name="item">The item.</param> @@ -120,40 +114,10 @@ namespace MediaBrowser.Server.Implementations.Providers cancellationToken.ThrowIfCancellationRequested(); - // Determine if supported providers have changed - var supportedProviders = MetadataProviders.Where(p => p.Supports(item)).ToList(); - - BaseProviderInfo supportedProvidersInfo; - - var supportedProvidersValue = string.Join(string.Empty, supportedProviders.Select(i => i.GetType().Name)); - var providersChanged = false; - - item.ProviderData.TryGetValue(_supportedProvidersKey, out supportedProvidersInfo); - - var supportedProvidersHash = supportedProvidersValue.GetMD5(); - - if (supportedProvidersInfo != null) - { - // Force refresh if the supported providers have changed - providersChanged = force = force || supportedProvidersHash != supportedProvidersInfo.Data; - - // If providers have changed, clear provider info and update the supported providers hash - if (providersChanged) - { - _logger.Debug("Providers changed for {0}. Clearing and forcing refresh.", item.Name); - item.ProviderData.Clear(); - } - } - - if (providersChanged) - { - supportedProvidersInfo.Data = supportedProvidersHash; - } - if (force) item.ClearMetaValues(); // Run the normal providers sequentially in order of priority - foreach (var provider in supportedProviders) + foreach (var provider in MetadataProviders.Where(p => p.Supports(item))) { cancellationToken.ThrowIfCancellationRequested(); @@ -206,12 +170,7 @@ namespace MediaBrowser.Server.Implementations.Providers result |= results.Contains(true); } - if (providersChanged) - { - item.ProviderData[_supportedProvidersKey] = supportedProvidersInfo; - } - - return result || providersChanged; + return result; } /// <summary> diff --git a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs index a45804f69..f8e47434e 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 /// </summary> private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); + /// <summary> + /// Gets the web socket connections. + /// </summary> + /// <value>The web socket connections.</value> + public IEnumerable<IWebSocketConnection> WebSocketConnections + { + get { return _webSocketConnections; } + } /// <summary> /// Gets or sets the external web socket server. @@ -83,6 +91,9 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// <value>The web socket listeners.</value> private readonly List<IWebSocketListener> _webSocketListeners = new List<IWebSocketListener>(); + /// <summary> + /// The _kernel + /// </summary> private readonly Kernel _kernel; /// <summary> @@ -240,7 +251,26 @@ namespace MediaBrowser.Server.Implementations.ServerManager /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">messageType</exception> - public async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken) + public Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken) + { + return SendWebSocketMessageAsync(messageType, dataFunction, _webSocketConnections, cancellationToken); + } + + /// <summary> + /// Sends the web socket message async. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="messageType">Type of the message.</param> + /// <param name="dataFunction">The data function.</param> + /// <param name="connections">The connections.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">messageType + /// or + /// dataFunction + /// or + /// cancellationToken</exception> + public async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> 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<T> { 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.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index 19d177a94..44e833c7a 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs @@ -82,8 +82,9 @@ namespace MediaBrowser.Server.Implementations.Session var vals = message.Data.Split('|'); session.NowViewingItemType = vals[0]; - session.NowViewingItemIdentifier = vals[1]; - session.NowViewingContext = vals.Length > 2 ? vals[2] : null; + session.NowViewingItemId = vals[1]; + session.NowViewingItemName = vals[2]; + session.NowViewingContext = vals.Length > 3 ? vals[3] : null; } } else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.ServerApplication/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.ServerApplication/EntryPoints/LibraryChangedNotifier.cs index 62c1e17f9..715492aac 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; + /// <summary> /// The _library changed sync lock /// </summary> private readonly object _libraryChangedSyncLock = new object(); - /// <summary> - /// Gets or sets the library update info. - /// </summary> - /// <value>The library update info.</value> - private LibraryUpdateInfo LibraryUpdateInfo { get; set; } + private readonly List<Folder> _foldersAddedTo = new List<Folder>(); + private readonly List<Folder> _foldersRemovedFrom = new List<Folder>(); + + private readonly List<BaseItem> _itemsAdded = new List<BaseItem>(); + private readonly List<BaseItem> _itemsRemoved = new List<BaseItem>(); + private readonly List<BaseItem> _itemsUpdated = new List<BaseItem>(); /// <summary> /// Gets or sets the library update timer. @@ -40,11 +46,12 @@ namespace MediaBrowser.ServerApplication.EntryPoints /// </summary> 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(); + } + } + + /// <summary> + /// Sends the change notifications. + /// </summary> + /// <param name="itemsAdded">The items added.</param> + /// <param name="itemsUpdated">The items updated.</param> + /// <param name="itemsRemoved">The items removed.</param> + /// <param name="foldersAddedTo">The folders added to.</param> + /// <param name="foldersRemovedFrom">The folders removed from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + private async void SendChangeNotifications(IEnumerable<BaseItem> itemsAdded, IEnumerable<BaseItem> itemsUpdated, IEnumerable<BaseItem> itemsRemoved, IEnumerable<Folder> foldersAddedTo, IEnumerable<Folder> 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); + } + } + + /// <summary> + /// Gets the library update info. + /// </summary> + /// <param name="itemsAdded">The items added.</param> + /// <param name="itemsUpdated">The items updated.</param> + /// <param name="itemsRemoved">The items removed.</param> + /// <param name="foldersAddedTo">The folders added to.</param> + /// <param name="foldersRemovedFrom">The folders removed from.</param> + /// <param name="userId">The user id.</param> + /// <returns>LibraryUpdateInfo.</returns> + private LibraryUpdateInfo GetLibraryUpdateInfo(IEnumerable<BaseItem> itemsAdded, IEnumerable<BaseItem> itemsUpdated, IEnumerable<BaseItem> itemsRemoved, IEnumerable<Folder> foldersAddedTo, IEnumerable<Folder> 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() + }; + } + + /// <summary> + /// Translates the physical item to user library. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="item">The item.</param> + /// <param name="user">The user.</param> + /// <param name="collections">The collections.</param> + /// <param name="allRecursiveChildren">All recursive children.</param> + /// <returns>IEnumerable{``0}.</returns> + private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, User user, List<BaseItem> collections, Dictionary<Guid, BaseItem> 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[] { }; } /// <summary> diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index a624f8e42..af0fa257d 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -130,6 +130,10 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.51\lib\net45\MediaBrowser.IsoMounter.dll</HintPath> </Reference> + <Reference Include="MoreLinq, Version=1.0.15631.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll</HintPath> + </Reference> <Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\NLog.2.0.1.2\lib\net45\NLog.dll</HintPath> diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index b411ab00e..1679a99fa 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -4,6 +4,7 @@ <package id="Hardcodet.Wpf.TaskbarNotification" version="1.0.4.0" targetFramework="net45" /> <package id="MahApps.Metro" version="0.11.0.17-ALPHA" targetFramework="net45" /> <package id="MediaBrowser.IsoMounting" version="3.0.51" targetFramework="net45" /> + <package id="morelinq" version="1.0.15631-beta" targetFramework="net45" /> <package id="NLog" version="2.0.1.2" targetFramework="net45" /> <package id="ServiceStack" version="3.9.46" targetFramework="net45" /> <package id="ServiceStack.Common" version="3.9.46" targetFramework="net45" /> diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 13b1bae99..e4955edc4 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.104</version> + <version>3.0.105</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.104" /> + <dependency id="MediaBrowser.Common" version="3.0.105" /> <dependency id="NLog" version="2.0.1.2" /> <dependency id="ServiceStack.Text" version="3.9.45" /> <dependency id="SimpleInjector" version="2.2.1" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index a8530230c..e43c7c227 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.104</version> + <version>3.0.105</version> <title>MediaBrowser.Common</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 8236cd633..5676f2ab6 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.104</version> + <version>3.0.105</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.104" /> + <dependency id="MediaBrowser.Common" version="3.0.105" /> </dependencies> </metadata> <files> |
