diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index f1e9e6f9a6..fbd0cfb1ef 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -11,6 +11,7 @@ using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -100,16 +101,24 @@ namespace MediaBrowser.Api [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")] + [ApiMember(Name = "Forced", Description = "Indicates if a normal or forced refresh should occur.", IsRequired = false, 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")] + [ApiMember(Name = "Recursive", Description = "Indicates if the refresh should occur recursively.", IsRequired = false, 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/{Id}", "DELETE")] + [Api(Description = "Deletes an item from the library and file system")] + public class DeleteItem : IReturnVoid + { + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + public string Id { get; set; } + } + [Route("/Items/Counts", "GET")] [Api(Description = "Gets counts of various item types")] public class GetItemCounts : IReturn @@ -223,9 +232,66 @@ namespace MediaBrowser.Api /// Posts the specified request. /// /// The request. - public void Post(RefreshLibrary request) + public async void Post(RefreshLibrary request) { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + try + { + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error refreshing library", ex); + } + } + + /// + /// Deletes the specified request. + /// + /// The request. + public async void Delete(DeleteItem request) + { + var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager); + + var parent = item.Parent; + + if (item.LocationType == LocationType.FileSystem) + { + if (Directory.Exists(item.Path)) + { + Directory.Delete(item.Path, true); + } + else if (File.Exists(item.Path)) + { + File.Delete(item.Path); + } + + if (parent != null) + { + try + { + await parent.ValidateChildren(new Progress(), CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error refreshing item", ex); + } + } + } + else if (parent != null) + { + try + { + await parent.RemoveChild(item, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error removing item", ex); + } + } + else + { + throw new InvalidOperationException("Don't know how to delete " + item.Name); + } } /// @@ -322,19 +388,26 @@ namespace MediaBrowser.Api /// Posts the specified request. /// /// The request. - public void Post(RefreshItem request) + public async void Post(RefreshItem request) { var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager); var folder = item as Folder; - if (folder != null) + try { - folder.ValidateChildren(new Progress(), CancellationToken.None, request.Recursive, request.Forced); + if (folder != null) + { + await folder.ValidateChildren(new Progress(), CancellationToken.None, request.Recursive, request.Forced).ConfigureAwait(false); + } + else + { + await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); + } } - else + catch (Exception ex) { - item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced); + Logger.ErrorException("Error refreshing library", ex); } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index c6a7470b7e..8b07145de4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -211,7 +211,7 @@ namespace MediaBrowser.Api.Playback /// MediaStream. private MediaStream GetMediaStream(IEnumerable allStream, int? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true) { - var streams = allStream.Where(s => s.Type == type).ToList(); + var streams = allStream.Where(s => s.Type == type).OrderBy(i => i.Index).ToList(); if (desiredIndex.HasValue) { diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 9afc24c016..1d7b4a4f3f 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -161,91 +161,93 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - 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) + using (var message = GetHttpRequestMessage(options)) { - await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); - } - - _logger.Info("HttpClientManager.Get url: {0}", options.Url); - - try - { - options.CancellationToken.ThrowIfCancellationRequested(); - - var response = await GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression).SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false); - - if (options.EnableResponseCache) + if (options.EnableResponseCache && cachedInfo != null) { - if (response.StatusCode != HttpStatusCode.NotModified) + if (!string.IsNullOrEmpty(cachedInfo.Etag)) { - EnsureSuccessStatusCode(response); + message.Headers.Add("If-None-Match", cachedInfo.Etag); } - - options.CancellationToken.ThrowIfCancellationRequested(); - - cachedInfo = UpdateInfoCache(cachedInfo, options.Url, cachedInfoPath, response); - - if (response.StatusCode == HttpStatusCode.NotModified) + else if (cachedInfo.LastModified.HasValue) { - _logger.Debug("Server indicates not modified for {0}. Returning cached result.", options.Url); - - return GetCachedResponse(cachedReponsePath); - } - - if (!string.IsNullOrEmpty(cachedInfo.Etag) || cachedInfo.LastModified.HasValue || - (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > DateTime.UtcNow)) - { - await UpdateResponseCache(response, cachedReponsePath).ConfigureAwait(false); - - return GetCachedResponse(cachedReponsePath); + message.Headers.IfModifiedSince = new DateTimeOffset(cachedInfo.LastModified.Value); } } - else - { - EnsureSuccessStatusCode(response); - options.CancellationToken.ThrowIfCancellationRequested(); - } - - 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) { - options.ResourcePool.Release(); + await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false); + } + + _logger.Info("HttpClientManager.Get url: {0}", options.Url); + + try + { + options.CancellationToken.ThrowIfCancellationRequested(); + + var response = await GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression).SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false); + + if (options.EnableResponseCache) + { + if (response.StatusCode != HttpStatusCode.NotModified) + { + EnsureSuccessStatusCode(response); + } + + options.CancellationToken.ThrowIfCancellationRequested(); + + cachedInfo = UpdateInfoCache(cachedInfo, options.Url, cachedInfoPath, response); + + if (response.StatusCode == HttpStatusCode.NotModified) + { + _logger.Debug("Server indicates not modified for {0}. Returning cached result.", options.Url); + + return GetCachedResponse(cachedReponsePath); + } + + if (!string.IsNullOrEmpty(cachedInfo.Etag) || cachedInfo.LastModified.HasValue || + (cachedInfo.Expires.HasValue && cachedInfo.Expires.Value > DateTime.UtcNow)) + { + await UpdateResponseCache(response, cachedReponsePath).ConfigureAwait(false); + + return GetCachedResponse(cachedReponsePath); + } + } + else + { + EnsureSuccessStatusCode(response); + + options.CancellationToken.ThrowIfCancellationRequested(); + } + + 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) + { + options.ResourcePool.Release(); + } } } + } /// diff --git a/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs index a925ad11d4..881065ea61 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs @@ -56,6 +56,30 @@ namespace MediaBrowser.Controller.Providers.MediaInfo ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.AudioImagesDataPath); } + /// + /// Gets a value indicating whether [refresh on version change]. + /// + /// true if [refresh on version change]; otherwise, false. + protected override bool RefreshOnVersionChange + { + get + { + return true; + } + } + + /// + /// Gets the provider version. + /// + /// The provider version. + protected override string ProviderVersion + { + get + { + return "1"; + } + } + /// /// Supportses the specified item. /// @@ -150,17 +174,17 @@ namespace MediaBrowser.Controller.Providers.MediaInfo { semaphore.Release(); } - - // Image is already in the cache - item.PrimaryImagePath = path; - - await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false); } else { semaphore.Release(); } } + + // Image is already in the cache + item.PrimaryImagePath = path; + + await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false); } /// diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 1069f6ef1d..3510ff9842 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -423,19 +423,6 @@ namespace MediaBrowser.Server.Implementations.Providers dataToSave.Position = 0; } - if (!(dataToSave is MemoryStream || dataToSave is FileStream)) - { - var ms = new MemoryStream(); - - using (dataToSave) - { - await dataToSave.CopyToAsync(ms).ConfigureAwait(false); - } - - ms.Position = 0; - dataToSave = ms; - } - try { using (dataToSave) diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index d3dbbc62b8..6c1768fd8e 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -220,6 +220,11 @@ namespace MediaBrowser.Server.Implementations.Session data.PlayCount++; data.LastPlayedDate = DateTime.UtcNow; + if (!(item is Video)) + { + data.Played = true; + } + await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false); // Nothing to save here