diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index dc93cb7304..3288229a2f 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -163,7 +163,7 @@ namespace Emby.Drawing return _imageEncoder.SupportedOutputFormats; } - public async Task> ProcessImage(ImageProcessingOptions options) + public async Task> ProcessImage(ImageProcessingOptions options) { if (options == null) { @@ -178,14 +178,13 @@ namespace Emby.Drawing } var originalImagePath = originalImage.Path; + var dateModified = originalImage.DateModified; if (!_imageEncoder.SupportsImageEncoding) { - return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath)); + return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } - var dateModified = originalImage.DateModified; - if (options.CropWhiteSpace && _imageEncoder.SupportsImageEncoding) { var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); @@ -211,7 +210,7 @@ namespace Emby.Drawing if (options.HasDefaultOptions(originalImagePath)) { // Just spit out the original file if all the options are default - return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath)); + return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } ImageSize? originalImageSize; @@ -221,7 +220,7 @@ namespace Emby.Drawing if (options.HasDefaultOptions(originalImagePath, originalImageSize.Value)) { // Just spit out the original file if all the options are default - return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath)); + return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } } catch @@ -235,10 +234,6 @@ namespace Emby.Drawing var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]); var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor, options.ForegroundLayer); - var semaphore = GetLock(cacheFilePath); - - await semaphore.WaitAsync().ConfigureAwait(false); - var imageProcessingLockTaken = false; try @@ -251,15 +246,20 @@ namespace Emby.Drawing var newHeight = Convert.ToInt32(newSize.Height); _fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); + var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")); + _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false); imageProcessingLockTaken = true; - _imageEncoder.EncodeImage(originalImagePath, cacheFilePath, AutoOrient(options.Item), newWidth, newHeight, quality, options, outputFormat); + _imageEncoder.EncodeImage(originalImagePath, tmpPath, AutoOrient(options.Item), newWidth, newHeight, quality, options, outputFormat); + CopyFile(tmpPath, cacheFilePath); + + return new Tuple(tmpPath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(tmpPath)); } - return new Tuple(cacheFilePath, GetMimeType(outputFormat, cacheFilePath)); + return new Tuple(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath)); } catch (Exception ex) { @@ -267,7 +267,7 @@ namespace Emby.Drawing _logger.ErrorException("Error encoding image", ex); // Just spit out the original file if all the options are default - return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath)); + return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } finally { @@ -275,8 +275,18 @@ namespace Emby.Drawing { _imageProcessingSemaphore.Release(); } + } + } + + private void CopyFile(string src, string destination) + { + try + { + File.Copy(src, destination, true); + } + catch + { - semaphore.Release(); } } @@ -412,14 +422,9 @@ namespace Emby.Drawing var croppedImagePath = GetCachePath(CroppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath)); - var semaphore = GetLock(croppedImagePath); - - await semaphore.WaitAsync().ConfigureAwait(false); - // Check again in case of contention if (_fileSystem.FileExists(croppedImagePath)) { - semaphore.Release(); return GetResult(croppedImagePath); } @@ -428,11 +433,15 @@ namespace Emby.Drawing try { _fileSystem.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); + var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")); + _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false); imageProcessingLockTaken = true; - _imageEncoder.CropWhiteSpace(originalImagePath, croppedImagePath); + _imageEncoder.CropWhiteSpace(originalImagePath, tmpPath); + CopyFile(tmpPath, croppedImagePath); + return GetResult(tmpPath); } catch (NotImplementedException) { @@ -452,11 +461,7 @@ namespace Emby.Drawing { _imageProcessingSemaphore.Release(); } - - semaphore.Release(); } - - return GetResult(croppedImagePath); } private Tuple GetResult(string path) diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index a549c44bc1..5866ad15bc 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -638,6 +638,7 @@ namespace MediaBrowser.Api.Images CacheDuration = cacheDuration, ResponseHeaders = headers, ContentType = imageResult.Item2, + DateLastModified = imageResult.Item3, IsHeadRequest = isHeadRequest, Path = imageResult.Item1, diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 4c9428cc44..bf543579f7 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -108,11 +108,11 @@ namespace MediaBrowser.Api.Playback.Progressive var eofCount = 0; long position = 0; - using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, false)) + using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { while (eofCount < 15) { - CopyToInternal(fs, outputStream, BufferSize); + await CopyToInternal(fs, outputStream, BufferSize).ConfigureAwait(false); var fsPosition = fs.Position; @@ -138,11 +138,11 @@ namespace MediaBrowser.Api.Playback.Progressive } } - private void CopyToInternal(Stream source, Stream destination, int bufferSize) + private async Task CopyToInternal(Stream source, Stream destination, int bufferSize) { var array = new byte[bufferSize]; int count; - while ((count = source.Read(array, 0, array.Length)) != 0) + while ((count = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) { //if (_job != null) //{ @@ -168,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Progressive // } //} - destination.Write(array, 0, count); + await destination.WriteAsync(array, 0, count).ConfigureAwait(false); _bytesWritten += count; diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 36a2a4b619..d0b6d6e787 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Reports /// Manager for library. private readonly ILibraryManager _libraryManager; ///< Manager for library - /// The localization. + /// The localization. private readonly ILocalizationManager _localization; ///< The localization @@ -58,10 +58,10 @@ namespace MediaBrowser.Api.Reports /// Gets the given request. /// The request. /// A Task<object> - public async Task Get(GetActivityLogs request) + public object Get(GetActivityLogs request) { request.DisplayType = "Screen"; - ReportResult result = await GetReportActivities(request).ConfigureAwait(false); + ReportResult result = GetReportActivities(request); return ToOptimizedResult(result); } @@ -104,7 +104,8 @@ namespace MediaBrowser.Api.Reports return null; request.DisplayType = "Screen"; - var reportResult = await GetReportResult(request); + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var reportResult = await GetReportResult(request, user); return ToOptimizedResult(reportResult); } @@ -117,7 +118,8 @@ namespace MediaBrowser.Api.Reports if (string.IsNullOrEmpty(request.IncludeItemTypes)) return null; request.DisplayType = "Screen"; - var reportResult = await GetReportStatistic(request); + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var reportResult = await GetReportStatistic(request, user); return ToOptimizedResult(reportResult); } @@ -150,6 +152,7 @@ namespace MediaBrowser.Api.Reports headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename); headers["Content-Encoding"] = "UTF-8"; + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; ReportResult result = null; switch (reportViewType) { @@ -157,12 +160,12 @@ namespace MediaBrowser.Api.Reports case ReportViewType.ReportData: ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); ReportBuilder dataBuilder = new ReportBuilder(_libraryManager); - QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); + QueryResult queryResult = await GetQueryResult(request, user).ConfigureAwait(false); result = dataBuilder.GetResult(queryResult.Items, request); result.TotalRecordCount = queryResult.TotalRecordCount; break; case ReportViewType.ReportActivities: - result = await GetReportActivities(request).ConfigureAwait(false); + result = GetReportActivities(request); break; } @@ -177,23 +180,15 @@ namespace MediaBrowser.Api.Reports break; } - object ro = ResultFactory.GetResult(returnResult, contentType, headers); - return ro; + return ResultFactory.GetResult(returnResult, contentType, headers); } #endregion - #region [Private Methods] - - /// Gets items query. - /// The request. - /// The user. - /// The items query. private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user) { - var query = new InternalItemsQuery + var query = new InternalItemsQuery(user) { - User = user, IsPlayed = request.IsPlayed, MediaTypes = request.GetMediaTypes(), IncludeItemTypes = request.GetIncludeItemTypes(), @@ -231,6 +226,7 @@ namespace MediaBrowser.Api.Reports Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), + GenreIds = request.GetGenreIds(), Studios = request.GetStudios(), StudioIds = request.GetStudioIds(), Person = request.Person, @@ -245,9 +241,11 @@ namespace MediaBrowser.Api.Reports MaxPlayers = request.MaxPlayers, MinCommunityRating = request.MinCommunityRating, MinCriticRating = request.MinCriticRating, + ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId), ParentIndexNumber = request.ParentIndexNumber, AiredDuringSeason = request.AiredDuringSeason, - AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater + AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater, + EnableTotalRecordCount = request.EnableTotalRecordCount }; if (!string.IsNullOrWhiteSpace(request.Ids)) @@ -357,98 +355,111 @@ namespace MediaBrowser.Api.Reports query.AlbumNames = request.Albums.Split('|'); } - if (request.HasQueryLimit == false) - { - query.StartIndex = null; - query.Limit = null; - } - return query; } - /// Gets query result. - /// The request. - /// The query result. - private async Task> GetQueryResult(BaseReportRequest request) + private async Task> GetQueryResult(BaseReportRequest request, User user) { - // Placeholder in case needed later + // all report queries currently need this because it's not being specified request.Recursive = true; - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts"; - - var parentItem = string.IsNullOrEmpty(request.ParentId) ? - (user == null ? _libraryManager.RootFolder : user.RootFolder) : - _libraryManager.GetItemById(request.ParentId); var item = string.IsNullOrEmpty(request.ParentId) ? user == null ? _libraryManager.RootFolder : user.RootFolder : - parentItem; + _libraryManager.GetItemById(request.ParentId); - IEnumerable items; + if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)) + { + //item = user == null ? _libraryManager.RootFolder : user.RootFolder; + } + else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) + { + item = user == null ? _libraryManager.RootFolder : user.RootFolder; + } - if (request.Recursive) + // Default list type = children + + var folder = item as Folder; + if (folder == null) { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); - return result; + folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder(); } - else + + if (!string.IsNullOrEmpty(request.Ids)) { - if (user == null) + request.Recursive = true; + var query = GetItemsQuery(request, user); + var result = await folder.GetItems(query).ConfigureAwait(false); + + if (string.IsNullOrWhiteSpace(request.SortBy)) { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); - return result; + var ids = query.ItemIds.ToList(); + + // Try to preserve order + result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray(); } - var userRoot = item as UserRootFolder; + return result; + } - if (userRoot == null) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); + if (request.Recursive) + { + return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); + } - return result; - } + if (user == null) + { + return await folder.GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); + } + + var userRoot = item as UserRootFolder; - items = ((Folder)item).GetChildren(user, true); + if (userRoot == null) + { + return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); } - return new QueryResult { Items = items.ToArray() }; + IEnumerable items = folder.GetChildren(user, true); + var itemsArray = items.ToArray(); + + return new QueryResult + { + Items = itemsArray, + TotalRecordCount = itemsArray.Length + }; } + #region [Private Methods] + /// Gets report activities. /// The request. /// The report activities. - private Task GetReportActivities(IReportsDownload request) + private ReportResult GetReportActivities(IReportsDownload request) { - return Task.Run(() => - { - DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? - (DateTime?)null : - DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - - QueryResult queryResult; - if (request.HasQueryLimit) - queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); - else - queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, null); - //var queryResult = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); - - ReportActivitiesBuilder builder = new ReportActivitiesBuilder(_libraryManager, _userManager); - var result = builder.GetResult(queryResult, request); - result.TotalRecordCount = queryResult.TotalRecordCount; - return result; + DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? + (DateTime?)null : + DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - }); + QueryResult queryResult; + if (request.HasQueryLimit) + queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); + else + queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, null); + //var queryResult = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); + ReportActivitiesBuilder builder = new ReportActivitiesBuilder(_libraryManager, _userManager); + var result = builder.GetResult(queryResult, request); + result.TotalRecordCount = queryResult.TotalRecordCount; + return result; } /// Gets report result. /// The request. /// The report result. - private async Task GetReportResult(GetItemReport request) + private async Task GetReportResult(GetItemReport request, User user) { ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); - QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); + QueryResult queryResult = await GetQueryResult(request, user).ConfigureAwait(false); ReportResult reportResult = reportBuilder.GetResult(queryResult.Items, request); reportResult.TotalRecordCount = queryResult.TotalRecordCount; @@ -458,10 +469,10 @@ namespace MediaBrowser.Api.Reports /// Gets report statistic. /// The request. /// The report statistic. - private async Task GetReportStatistic(GetReportStatistics request) + private async Task GetReportStatistic(GetReportStatistics request, User user) { ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); + QueryResult queryResult = await GetQueryResult(request, user).ConfigureAwait(false); ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager); ReportStatResult reportResult = reportBuilder.GetResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index a6f1d8b98f..57acb07ac8 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -101,7 +101,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - var result = await GetItemsToSerialize(request, user).ConfigureAwait(false); + var result = await GetQueryResult(request, user).ConfigureAwait(false); if (result == null) { @@ -135,7 +135,7 @@ namespace MediaBrowser.Api.UserLibrary /// The request. /// The user. /// IEnumerable{BaseItem}. - private async Task> GetItemsToSerialize(GetItems request, User user) + private async Task> GetQueryResult(GetItems request, User user) { var item = string.IsNullOrEmpty(request.ParentId) ? user == null ? _libraryManager.RootFolder : user.RootFolder : diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index d42a04f2ee..19f391b4a6 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.Drawing /// /// The options. /// Task. - Task> ProcessImage(ImageProcessingOptions options); + Task> ProcessImage(ImageProcessingOptions options); /// /// Gets the enhanced image. diff --git a/MediaBrowser.Model.Portable/FodyWeavers.xml b/MediaBrowser.Model.Portable/FodyWeavers.xml index 7369928101..6e2fa02e64 100644 --- a/MediaBrowser.Model.Portable/FodyWeavers.xml +++ b/MediaBrowser.Model.Portable/FodyWeavers.xml @@ -1,4 +1,3 @@ - + - \ No newline at end of file diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 0de9fb519c..862d95f7ee 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -36,6 +36,8 @@ false true true + + true @@ -617,9 +619,6 @@ Extensions\FloatHelper.cs - - Extensions\IHasPropertyChangedEvent.cs - Extensions\IntHelper.cs @@ -1233,13 +1232,6 @@ - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - -