From e7be01d7a5b7d2e93d8ee0fddb812c2ce048db50 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 22 Jan 2022 23:36:42 +0100 Subject: [PATCH] Flush to disk async where possible --- Emby.Dlna/DlnaManager.cs | 3 +- Emby.Dlna/PlayTo/PlayToController.cs | 10 +- .../Library/LibraryManager.cs | 6 +- .../LiveTv/EmbyTV/DirectRecorder.cs | 20 +- .../LiveTv/EmbyTV/EmbyTV.cs | 126 ++++---- .../HdHomerun/HdHomerunUdpStream.cs | 3 +- .../LiveTv/TunerHosts/LiveStream.cs | 18 +- ...linkFollowingPhysicalFileResultExecutor.cs | 14 +- Jellyfin.Server/Jellyfin.Server.csproj | 4 - Jellyfin.Server/Program.cs | 13 +- .../Library/IMetadataFileSaver.cs | 5 - .../Library/IMetadataSaver.cs | 6 +- .../Providers/IProviderManager.cs | 6 +- .../Savers/BaseXmlSaver.cs | 284 ++++++++---------- .../Savers/BoxSetXmlSaver.cs | 6 +- .../Savers/PlaylistXmlSaver.cs | 9 +- .../Subtitles/SubtitleEncoder.cs | 6 +- .../Manager/ProviderManager.cs | 22 +- .../Subtitles/SubtitleManager.cs | 7 +- MediaBrowser.XbmcMetadata/EntryPoint.cs | 6 +- .../Savers/BaseNfoSaver.cs | 15 +- 21 files changed, 291 insertions(+), 298 deletions(-) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index f2a0548c2d..192128c7ec 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -343,7 +343,8 @@ namespace Emby.Dlna var fileOptions = AsyncFile.WriteOptions; fileOptions.Mode = FileMode.Create; fileOptions.PreallocationSize = length; - using (var fileStream = new FileStream(path, fileOptions)) + var fileStream = new FileStream(path, fileOptions); + await using (fileStream.ConfigureAwait(false)) { await stream.CopyToAsync(fileStream).ConfigureAwait(false); } diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index d0c9df68e3..23f2456ac2 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -180,7 +180,7 @@ namespace Emby.Dlna.PlayTo _currentPlaylistIndex = currentItemIndex; } - await SendNextTrackMessage(currentItemIndex, CancellationToken.None); + await SendNextTrackMessage(currentItemIndex, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -453,7 +453,7 @@ namespace Emby.Dlna.PlayTo // Send a message to the DLNA device to notify what is the next track in the play list. var newItemIndex = _playlist.FindIndex(item => item.StreamUrl == newItem.StreamUrl); - await SendNextTrackMessage(newItemIndex, CancellationToken.None); + await SendNextTrackMessage(newItemIndex, CancellationToken.None).ConfigureAwait(false); return; } @@ -654,7 +654,7 @@ namespace Emby.Dlna.PlayTo await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, cancellationToken).ConfigureAwait(false); // Send a message to the DLNA device to notify what is the next track in the play list. - await SendNextTrackMessage(index, cancellationToken); + await SendNextTrackMessage(index, cancellationToken).ConfigureAwait(false); var streamInfo = currentitem.StreamInfo; if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo)) @@ -771,7 +771,7 @@ namespace Emby.Dlna.PlayTo // Send a message to the DLNA device to notify what is the next track in the play list. var newItemIndex = _playlist.FindIndex(item => item.StreamUrl == newItem.StreamUrl); - await SendNextTrackMessage(newItemIndex, CancellationToken.None); + await SendNextTrackMessage(newItemIndex, CancellationToken.None).ConfigureAwait(false); if (EnableClientSideSeek(newItem.StreamInfo)) { @@ -800,7 +800,7 @@ namespace Emby.Dlna.PlayTo // Send a message to the DLNA device to notify what is the next track in the play list. var newItemIndex = _playlist.FindIndex(item => item.StreamUrl == newItem.StreamUrl); - await SendNextTrackMessage(newItemIndex, CancellationToken.None); + await SendNextTrackMessage(newItemIndex, CancellationToken.None).ConfigureAwait(false); if (EnableClientSideSeek(newItem.StreamInfo) && newPosition > 0) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 262d9fed7e..c2ebcf192f 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2014,16 +2014,16 @@ namespace Emby.Server.Implementations.Library public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken); - public Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason) + public async Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason) { if (item.IsFileProtocol) { - ProviderManager.SaveMetadata(item, updateReason); + await ProviderManager.SaveMetadataAsync(item, updateReason).ConfigureAwait(false); } item.DateLastSaved = DateTime.UtcNow; - return UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate); + await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false); } /// diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 6937cc0971..b2d25fdaeb 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile))); - using (var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) + await using (var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) { onStarted(); @@ -56,14 +56,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV using var durationToken = new CancellationTokenSource(duration); using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token); var linkedCancellationToken = cancellationTokenSource.Token; - - await using var fileStream = new ProgressiveFileStream(directStreamProvider.GetStream()); - await _streamHelper.CopyToAsync( - fileStream, - output, - IODefaults.CopyToBufferSize, - 1000, - linkedCancellationToken).ConfigureAwait(false); + var fileStream = new ProgressiveFileStream(directStreamProvider.GetStream()); + await using (fileStream.ConfigureAwait(false)) + { + await _streamHelper.CopyToAsync( + fileStream, + output, + IODefaults.CopyToBufferSize, + 1000, + linkedCancellationToken).ConfigureAwait(false); + } } _logger.LogInformation("Recording completed: {FilePath}", targetFile); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index e7834ffd64..bba5848542 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1819,16 +1819,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (timer.IsProgramSeries) { - SaveSeriesNfo(timer, seriesPath); - SaveVideoNfo(timer, recordingPath, program, false); + await SaveSeriesNfoAsync(timer, seriesPath).ConfigureAwait(false); + await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false); } else if (!timer.IsMovie || timer.IsSports || timer.IsNews) { - SaveVideoNfo(timer, recordingPath, program, true); + await SaveVideoNfoAsync(timer, recordingPath, program, true).ConfigureAwait(false); } else { - SaveVideoNfo(timer, recordingPath, program, false); + await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false); } await SaveRecordingImages(recordingPath, program).ConfigureAwait(false); @@ -1839,7 +1839,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private void SaveSeriesNfo(TimerInfo timer, string seriesPath) + private async Task SaveSeriesNfoAsync(TimerInfo timer, string seriesPath) { var nfoPath = Path.Combine(seriesPath, "tvshow.nfo"); @@ -1848,61 +1848,62 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) + await using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { var settings = new XmlWriterSettings { Indent = true, - Encoding = Encoding.UTF8 + Encoding = Encoding.UTF8, + Async = true }; - using (var writer = XmlWriter.Create(stream, settings)) + await using (var writer = XmlWriter.Create(stream, settings)) { - writer.WriteStartDocument(true); - writer.WriteStartElement("tvshow"); + await writer.WriteStartDocumentAsync(true).ConfigureAwait(false); + await writer.WriteStartElementAsync(null, "tvshow", null).ConfigureAwait(false); string id; if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id)) { - writer.WriteElementString("id", id); + await writer.WriteElementStringAsync(null, "id", null, id).ConfigureAwait(false); } if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out id)) { - writer.WriteElementString("imdb_id", id); + await writer.WriteElementStringAsync(null, "imdb_id", null, id).ConfigureAwait(false); } if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out id)) { - writer.WriteElementString("tmdbid", id); + await writer.WriteElementStringAsync(null, "tmdbid", null, id).ConfigureAwait(false); } if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out id)) { - writer.WriteElementString("zap2itid", id); + await writer.WriteElementStringAsync(null, "zap2itid", null, id).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(timer.Name)) { - writer.WriteElementString("title", timer.Name); + await writer.WriteElementStringAsync(null, "title", null, timer.Name).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(timer.OfficialRating)) { - writer.WriteElementString("mpaa", timer.OfficialRating); + await writer.WriteElementStringAsync(null, "mpaa", null, timer.OfficialRating).ConfigureAwait(false); } foreach (var genre in timer.Genres) { - writer.WriteElementString("genre", genre); + await writer.WriteElementStringAsync(null, "genre", null, genre).ConfigureAwait(false); } - writer.WriteEndElement(); - writer.WriteEndDocument(); + await writer.WriteEndElementAsync().ConfigureAwait(false); + await writer.WriteEndDocumentAsync().ConfigureAwait(false); } } } - private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData) + private async Task SaveVideoNfoAsync(TimerInfo timer, string recordingPath, BaseItem item, bool lockData) { var nfoPath = Path.ChangeExtension(recordingPath, ".nfo"); @@ -1911,29 +1912,30 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) + await using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { var settings = new XmlWriterSettings { Indent = true, - Encoding = Encoding.UTF8 + Encoding = Encoding.UTF8, + Async = true }; var options = _config.GetNfoConfiguration(); var isSeriesEpisode = timer.IsProgramSeries; - using (var writer = XmlWriter.Create(stream, settings)) + await using (var writer = XmlWriter.Create(stream, settings)) { - writer.WriteStartDocument(true); + await writer.WriteStartDocumentAsync(true).ConfigureAwait(false); if (isSeriesEpisode) { - writer.WriteStartElement("episodedetails"); + await writer.WriteStartElementAsync(null, "episodedetails", null).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(timer.EpisodeTitle)) { - writer.WriteElementString("title", timer.EpisodeTitle); + await writer.WriteElementStringAsync(null, "title", null, timer.EpisodeTitle).ConfigureAwait(false); } var premiereDate = item.PremiereDate ?? (!timer.IsRepeat ? DateTime.UtcNow : null); @@ -1942,76 +1944,84 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var formatString = options.ReleaseDateFormat; - writer.WriteElementString( + await writer.WriteElementStringAsync( + null, "aired", - premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)); + null, + premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (item.IndexNumber.HasValue) { - writer.WriteElementString("episode", item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "episode", null, item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (item.ParentIndexNumber.HasValue) { - writer.WriteElementString("season", item.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "season", null, item.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } } else { - writer.WriteStartElement("movie"); + await writer.WriteStartElementAsync(null, "movie", null); if (!string.IsNullOrWhiteSpace(item.Name)) { - writer.WriteElementString("title", item.Name); + await writer.WriteElementStringAsync(null, "title", null, item.Name).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(item.OriginalTitle)) { - writer.WriteElementString("originaltitle", item.OriginalTitle); + await writer.WriteElementStringAsync(null, "originaltitle", null, item.OriginalTitle).ConfigureAwait(false); } if (item.PremiereDate.HasValue) { var formatString = options.ReleaseDateFormat; - writer.WriteElementString( + await writer.WriteElementStringAsync( + null, "premiered", - item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)); - writer.WriteElementString( + null, + item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)).ConfigureAwait(false); + await writer.WriteElementStringAsync( + null, "releasedate", - item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)); + null, + item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)).ConfigureAwait(false); } } - writer.WriteElementString( + await writer.WriteElementStringAsync( + null, "dateadded", - DateTime.Now.ToString(DateAddedFormat, CultureInfo.InvariantCulture)); + null, + DateTime.Now.ToString(DateAddedFormat, CultureInfo.InvariantCulture)).ConfigureAwait(false); if (item.ProductionYear.HasValue) { - writer.WriteElementString("year", item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "year", null, item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (!string.IsNullOrEmpty(item.OfficialRating)) { - writer.WriteElementString("mpaa", item.OfficialRating); + await writer.WriteElementStringAsync(null, "mpaa", null, item.OfficialRating).ConfigureAwait(false); } var overview = (item.Overview ?? string.Empty) .StripHtml() .Replace(""", "'", StringComparison.Ordinal); - writer.WriteElementString("plot", overview); + await writer.WriteElementStringAsync(null, "plot", null, overview).ConfigureAwait(false); if (item.CommunityRating.HasValue) { - writer.WriteElementString("rating", item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "rating", null, item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } foreach (var genre in item.Genres) { - writer.WriteElementString("genre", genre); + await writer.WriteElementStringAsync(null, "genre", null, genre).ConfigureAwait(false); } var people = item.Id.Equals(Guid.Empty) ? new List() : _libraryManager.GetPeople(item); @@ -2023,7 +2033,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV foreach (var person in directors) { - writer.WriteElementString("director", person); + await writer.WriteElementStringAsync(null, "director", null, person).ConfigureAwait(false); } var writers = people @@ -2034,19 +2044,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV foreach (var person in writers) { - writer.WriteElementString("writer", person); + await writer.WriteElementStringAsync(null, "writer", null, person).ConfigureAwait(false); } foreach (var person in writers) { - writer.WriteElementString("credits", person); + await writer.WriteElementStringAsync(null, "credits", null, person).ConfigureAwait(false); } var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection); if (!string.IsNullOrEmpty(tmdbCollection)) { - writer.WriteElementString("collectionnumber", tmdbCollection); + await writer.WriteElementStringAsync(null, "collectionnumber", null, tmdbCollection).ConfigureAwait(false); } var imdb = item.GetProviderId(MetadataProvider.Imdb); @@ -2054,10 +2064,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (!isSeriesEpisode) { - writer.WriteElementString("id", imdb); + await writer.WriteElementStringAsync(null, "id", null, imdb).ConfigureAwait(false); } - writer.WriteElementString("imdbid", imdb); + await writer.WriteElementStringAsync(null, "imdbid", null, imdb).ConfigureAwait(false); // No need to lock if we have identified the content already lockData = false; @@ -2066,7 +2076,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var tvdb = item.GetProviderId(MetadataProvider.Tvdb); if (!string.IsNullOrEmpty(tvdb)) { - writer.WriteElementString("tvdbid", tvdb); + await writer.WriteElementStringAsync(null, "tvdbid", null, tvdb).ConfigureAwait(false); // No need to lock if we have identified the content already lockData = false; @@ -2075,7 +2085,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var tmdb = item.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(tmdb)) { - writer.WriteElementString("tmdbid", tmdb); + await writer.WriteElementStringAsync(null, "tmdbid", null, tmdb).ConfigureAwait(false); // No need to lock if we have identified the content already lockData = false; @@ -2083,26 +2093,26 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (lockData) { - writer.WriteElementString("lockdata", true.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); + await writer.WriteElementStringAsync(null, "lockdata", null, "true").ConfigureAwait(false); } if (item.CriticRating.HasValue) { - writer.WriteElementString("criticrating", item.CriticRating.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "criticrating", null, item.CriticRating.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(item.Tagline)) { - writer.WriteElementString("tagline", item.Tagline); + await writer.WriteElementStringAsync(null, "tagline", null, item.Tagline).ConfigureAwait(false); } foreach (var studio in item.Studios) { - writer.WriteElementString("studio", studio); + await writer.WriteElementStringAsync(null, "studio", null, studio).ConfigureAwait(false); } - writer.WriteEndElement(); - writer.WriteEndDocument(); + await writer.WriteEndElementAsync().ConfigureAwait(false); + await writer.WriteEndDocumentAsync().ConfigureAwait(false); } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index a5edd35cce..d256283c55 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -186,7 +186,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var resolved = false; - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) + var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read); + await using (fileStream.ConfigureAwait(false)) { while (true) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 5581ba87c5..2748794b38 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -97,7 +97,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public Stream GetStream() { - var stream = GetInputStream(TempFilePath); + var stream = new FileStream( + TempFilePath, + FileMode.Open, + FileAccess.Read, + FileShare.ReadWrite, + IODefaults.FileStreamBufferSize, + FileOptions.SequentialScan | FileOptions.Asynchronous); + bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10; if (seekFile) { @@ -107,15 +114,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return stream; } - protected FileStream GetInputStream(string path) - => new FileStream( - path, - FileMode.Open, - FileAccess.Read, - FileShare.ReadWrite, - IODefaults.FileStreamBufferSize, - FileOptions.SequentialScan | FileOptions.Asynchronous); - protected async Task DeleteTempFiles(string path, int retryCount = 0) { if (retryCount == 0) diff --git a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs index 73a619b8d9..7e7e4ca95b 100644 --- a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs +++ b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs @@ -125,18 +125,20 @@ namespace Jellyfin.Server.Infrastructure // Copied from SendFileFallback.SendFileAsync const int BufferSize = 1024 * 16; - await using var fileStream = new FileStream( + var fileStream = new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize: BufferSize, options: FileOptions.Asynchronous | FileOptions.SequentialScan); - - fileStream.Seek(offset, SeekOrigin.Begin); - await StreamCopyOperation - .CopyToAsync(fileStream, response.Body, count, BufferSize, CancellationToken.None) - .ConfigureAwait(true); + await using (fileStream.ConfigureAwait(false)) + { + fileStream.Seek(offset, SeekOrigin.Begin); + await StreamCopyOperation + .CopyToAsync(fileStream, response.Body, count, BufferSize, CancellationToken.None) + .ConfigureAwait(true); + } } private static bool IsSymLink(string path) => (File.GetAttributes(path) & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint; diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index d625a223be..0529883b98 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -14,10 +14,6 @@ true - - false - - diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index f40526e223..2929355ec9 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -545,12 +545,15 @@ namespace Jellyfin.Server // Get a stream of the resource contents // NOTE: The .csproj name is used instead of the assembly name in the resource path const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; - await using Stream resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath) + Stream resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath) ?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'"); - - // Copy the resource contents to the expected file path for the config file - await using Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - await resource.CopyToAsync(dst).ConfigureAwait(false); + Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); + await using (resource.ConfigureAwait(false)) + await using (dst.ConfigureAwait(false)) + { + // Copy the resource contents to the expected file path for the config file + await resource.CopyToAsync(dst).ConfigureAwait(false); + } } /// diff --git a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs index 9c6f03a232..842c687d1d 100644 --- a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs @@ -13,9 +13,4 @@ namespace MediaBrowser.Controller.Library /// System.String. string GetSavePath(BaseItem item); } - - public interface IConfigurableProvider - { - bool IsEnabled { get; } - } } diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index d963fd2491..eed6613459 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -1,6 +1,5 @@ -#nullable disable - using System.Threading; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Library @@ -29,6 +28,7 @@ namespace MediaBrowser.Controller.Library /// /// The item. /// The cancellation token. - void Save(BaseItem item, CancellationToken cancellationToken); + /// The task object representing the asynchronous operation. + Task SaveAsync(BaseItem item, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 9f7a76be64..44bc4a50cb 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -156,7 +156,8 @@ namespace MediaBrowser.Controller.Providers /// /// The item. /// Type of the update. - void SaveMetadata(BaseItem item, ItemUpdateType updateType); + /// The task object representing the asynchronous operation. + Task SaveMetadataAsync(BaseItem item, ItemUpdateType updateType); /// /// Saves the metadata. @@ -164,7 +165,8 @@ namespace MediaBrowser.Controller.Providers /// The item. /// Type of the update. /// The metadata savers. - void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable savers); + /// The task object representing the asynchronous operation. + Task SaveMetadataAsync(BaseItem item, ItemUpdateType updateType, IEnumerable savers); /// /// Gets the metadata options. diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index 1a8b5bb4ee..2d94c5de85 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading; +using System.Threading.Tasks; using System.Xml; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -20,11 +21,6 @@ namespace MediaBrowser.LocalMetadata.Savers /// public abstract class BaseXmlSaver : IMetadataFileSaver { - /// - /// Gets the date added format. - /// - public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss"; - /// /// Initializes a new instance of the class. /// @@ -82,9 +78,7 @@ namespace MediaBrowser.LocalMetadata.Savers /// The item. /// System.String. protected virtual string GetRootElementName(BaseItem item) - { - return "Item"; - } + => "Item"; /// /// Determines whether [is enabled for] [the specified item]. @@ -95,23 +89,10 @@ namespace MediaBrowser.LocalMetadata.Savers public abstract bool IsEnabledFor(BaseItem item, ItemUpdateType updateType); /// - public void Save(BaseItem item, CancellationToken cancellationToken) + public async Task SaveAsync(BaseItem item, CancellationToken cancellationToken) { var path = GetSavePath(item); - - using var memoryStream = new MemoryStream(); - Save(item, memoryStream); - - memoryStream.Position = 0; - - cancellationToken.ThrowIfCancellationRequested(); - - SaveToFile(memoryStream, path); - } - - private void SaveToFile(Stream stream, string path) - { - var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"Provided path ({path}) is not valid.", nameof(path)); + var directory = Path.GetDirectoryName(path) ?? throw new InvalidDataException($"Provided path ({path}) is not valid."); Directory.CreateDirectory(directory); // On Windows, savint the file will fail if the file is hidden or readonly @@ -121,13 +102,41 @@ namespace MediaBrowser.LocalMetadata.Savers { Mode = FileMode.Create, Access = FileAccess.Write, - Share = FileShare.None, - PreallocationSize = stream.Length + Share = FileShare.None }; - using (var filestream = new FileStream(path, fileStreamOptions)) + var filestream = new FileStream(path, fileStreamOptions); + await using (filestream.ConfigureAwait(false)) { - stream.CopyTo(filestream); + var settings = new XmlWriterSettings + { + Indent = true, + Encoding = Encoding.UTF8, + Async = true + }; + + var writer = XmlWriter.Create(filestream, settings); + await using (writer.ConfigureAwait(false)) + { + var root = GetRootElementName(item); + + await writer.WriteStartDocumentAsync(true).ConfigureAwait(false); + + await writer.WriteStartElementAsync(null, root, null).ConfigureAwait(false); + + var baseItem = item; + + if (baseItem != null) + { + await AddCommonNodesAsync(baseItem, writer).ConfigureAwait(false); + } + + await WriteCustomElementsAsync(item, writer).ConfigureAwait(false); + + await writer.WriteEndElementAsync().ConfigureAwait(false); + + await writer.WriteEndDocumentAsync().ConfigureAwait(false); + } } if (ConfigurationManager.Configuration.SaveMetadataHidden) @@ -148,107 +157,76 @@ namespace MediaBrowser.LocalMetadata.Savers } } - private void Save(BaseItem item, Stream stream) - { - var settings = new XmlWriterSettings - { - Indent = true, - Encoding = Encoding.UTF8, - CloseOutput = false - }; - - using (var writer = XmlWriter.Create(stream, settings)) - { - var root = GetRootElementName(item); - - writer.WriteStartDocument(true); - - writer.WriteStartElement(root); - - var baseItem = item; - - if (baseItem != null) - { - AddCommonNodes(baseItem, writer, LibraryManager); - } - - WriteCustomElements(item, writer); - - writer.WriteEndElement(); - - writer.WriteEndDocument(); - } - } - /// /// Write custom elements. /// /// The item. /// The xml writer. - protected abstract void WriteCustomElements(BaseItem item, XmlWriter writer); + /// The task object representing the asynchronous operation. + protected abstract Task WriteCustomElementsAsync(BaseItem item, XmlWriter writer); /// /// Adds the common nodes. /// /// The item. /// The xml writer. - /// Instance of the interface. - public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager) + /// The task object representing the asynchronous operation. + private async Task AddCommonNodesAsync(BaseItem item, XmlWriter writer) { if (!string.IsNullOrEmpty(item.OfficialRating)) { - writer.WriteElementString("ContentRating", item.OfficialRating); + await writer.WriteElementStringAsync(null, "ContentRating", null, item.OfficialRating).ConfigureAwait(false); } - writer.WriteElementString("Added", item.DateCreated.ToLocalTime().ToString("G", CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "Added", null, item.DateCreated.ToLocalTime().ToString("G", CultureInfo.InvariantCulture)).ConfigureAwait(false); - writer.WriteElementString("LockData", item.IsLocked.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); + await writer.WriteElementStringAsync(null, "LockData", null, item.IsLocked.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()).ConfigureAwait(false); if (item.LockedFields.Length > 0) { - writer.WriteElementString("LockedFields", string.Join('|', item.LockedFields)); + await writer.WriteElementStringAsync(null, "LockedFields", null, string.Join('|', item.LockedFields)).ConfigureAwait(false); } if (item.CriticRating.HasValue) { - writer.WriteElementString("CriticRating", item.CriticRating.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "CriticRating", null, item.CriticRating.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (!string.IsNullOrEmpty(item.Overview)) { - writer.WriteElementString("Overview", item.Overview); + await writer.WriteElementStringAsync(null, "Overview", null, item.Overview).ConfigureAwait(false); } if (!string.IsNullOrEmpty(item.OriginalTitle)) { - writer.WriteElementString("OriginalTitle", item.OriginalTitle); + await writer.WriteElementStringAsync(null, "OriginalTitle", null, item.OriginalTitle).ConfigureAwait(false); } if (!string.IsNullOrEmpty(item.CustomRating)) { - writer.WriteElementString("CustomRating", item.CustomRating); + await writer.WriteElementStringAsync(null, "CustomRating", null, item.CustomRating).ConfigureAwait(false); } if (!string.IsNullOrEmpty(item.Name) && item is not Episode) { - writer.WriteElementString("LocalTitle", item.Name); + await writer.WriteElementStringAsync(null, "LocalTitle", null, item.Name).ConfigureAwait(false); } var forcedSortName = item.ForcedSortName; if (!string.IsNullOrEmpty(forcedSortName)) { - writer.WriteElementString("SortTitle", forcedSortName); + await writer.WriteElementStringAsync(null, "SortTitle", null, forcedSortName).ConfigureAwait(false); } if (item.PremiereDate.HasValue) { if (item is Person) { - writer.WriteElementString("BirthDate", item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "BirthDate", null, item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)).ConfigureAwait(false); } else if (item is not Episode) { - writer.WriteElementString("PremiereDate", item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "PremiereDate", null, item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)).ConfigureAwait(false); } } @@ -256,69 +234,69 @@ namespace MediaBrowser.LocalMetadata.Savers { if (item is Person) { - writer.WriteElementString("DeathDate", item.EndDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "DeathDate", null, item.EndDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)).ConfigureAwait(false); } else if (item is not Episode) { - writer.WriteElementString("EndDate", item.EndDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "EndDate", null, item.EndDate.Value.ToLocalTime().ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)).ConfigureAwait(false); } } if (item.RemoteTrailers.Count > 0) { - writer.WriteStartElement("Trailers"); + await writer.WriteStartElementAsync(null, "Trailers", null).ConfigureAwait(false); foreach (var trailer in item.RemoteTrailers) { - writer.WriteElementString("Trailer", trailer.Url); + await writer.WriteElementStringAsync(null, "Trailer", null, trailer.Url).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } if (item.ProductionLocations.Length > 0) { - writer.WriteStartElement("Countries"); + await writer.WriteStartElementAsync(null, "Countries", null).ConfigureAwait(false); foreach (var name in item.ProductionLocations) { - writer.WriteElementString("Country", name); + await writer.WriteElementStringAsync(null, "Country", null, name).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } if (item is IHasDisplayOrder hasDisplayOrder && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder)) { - writer.WriteElementString("DisplayOrder", hasDisplayOrder.DisplayOrder); + await writer.WriteElementStringAsync(null, "DisplayOrder", null, hasDisplayOrder.DisplayOrder).ConfigureAwait(false); } if (item.CommunityRating.HasValue) { - writer.WriteElementString("Rating", item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "Rating", null, item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (item.ProductionYear.HasValue && item is not Person) { - writer.WriteElementString("ProductionYear", item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "ProductionYear", null, item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (item is IHasAspectRatio hasAspectRatio) { if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio)) { - writer.WriteElementString("AspectRatio", hasAspectRatio.AspectRatio); + await writer.WriteElementStringAsync(null, "AspectRatio", null, hasAspectRatio.AspectRatio).ConfigureAwait(false); } } if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage)) { - writer.WriteElementString("Language", item.PreferredMetadataLanguage); + await writer.WriteElementStringAsync(null, "Language", null, item.PreferredMetadataLanguage).ConfigureAwait(false); } if (!string.IsNullOrEmpty(item.PreferredMetadataCountryCode)) { - writer.WriteElementString("CountryCode", item.PreferredMetadataCountryCode); + await writer.WriteElementStringAsync(null, "CountryCode", null, item.PreferredMetadataCountryCode).ConfigureAwait(false); } // Use original runtime here, actual file runtime later in MediaInfo @@ -328,7 +306,7 @@ namespace MediaBrowser.LocalMetadata.Savers { var timespan = TimeSpan.FromTicks(runTimeTicks.Value); - writer.WriteElementString("RunningTime", Math.Floor(timespan.TotalMinutes).ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "RunningTime", null, Math.Floor(timespan.TotalMinutes).ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } if (item.ProviderIds != null) @@ -338,94 +316,94 @@ namespace MediaBrowser.LocalMetadata.Savers var providerId = item.ProviderIds[providerKey]; if (!string.IsNullOrEmpty(providerId)) { - writer.WriteElementString(providerKey + "Id", providerId); + await writer.WriteElementStringAsync(null, providerKey + "Id", null, providerId).ConfigureAwait(false); } } } if (!string.IsNullOrWhiteSpace(item.Tagline)) { - writer.WriteStartElement("Taglines"); - writer.WriteElementString("Tagline", item.Tagline); - writer.WriteEndElement(); + await writer.WriteStartElementAsync(null, "Taglines", null).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "Tagline", null, item.Tagline).ConfigureAwait(false); + await writer.WriteEndElementAsync().ConfigureAwait(false); } if (item.Genres.Length > 0) { - writer.WriteStartElement("Genres"); + await writer.WriteStartElementAsync(null, "Genres", null).ConfigureAwait(false); foreach (var genre in item.Genres) { - writer.WriteElementString("Genre", genre); + await writer.WriteElementStringAsync(null, "Genre", null, genre).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } if (item.Studios.Length > 0) { - writer.WriteStartElement("Studios"); + await writer.WriteStartElementAsync(null, "Studios", null).ConfigureAwait(false); foreach (var studio in item.Studios) { - writer.WriteElementString("Studio", studio); + await writer.WriteElementStringAsync(null, "Studio", null, studio).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } if (item.Tags.Length > 0) { - writer.WriteStartElement("Tags"); + await writer.WriteStartElementAsync(null, "Tags", null).ConfigureAwait(false); foreach (var tag in item.Tags) { - writer.WriteElementString("Tag", tag); + await writer.WriteElementStringAsync(null, "Tag", null, tag).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } - var people = libraryManager.GetPeople(item); + var people = LibraryManager.GetPeople(item); if (people.Count > 0) { - writer.WriteStartElement("Persons"); + await writer.WriteStartElementAsync(null, "Persons", null).ConfigureAwait(false); foreach (var person in people) { - writer.WriteStartElement("Person"); - writer.WriteElementString("Name", person.Name); - writer.WriteElementString("Type", person.Type); - writer.WriteElementString("Role", person.Role); + await writer.WriteStartElementAsync(null, "Person", null).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "Name", null, person.Name).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "Type", null, person.Type).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "Role", null, person.Role).ConfigureAwait(false); if (person.SortOrder.HasValue) { - writer.WriteElementString("SortOrder", person.SortOrder.Value.ToString(CultureInfo.InvariantCulture)); + await writer.WriteElementStringAsync(null, "SortOrder", null, person.SortOrder.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } if (item is BoxSet boxset) { - AddLinkedChildren(boxset, writer, "CollectionItems", "CollectionItem"); + await AddLinkedChildren(boxset, writer, "CollectionItems", "CollectionItem").ConfigureAwait(false); } if (item is Playlist playlist && !Playlist.IsPlaylistFile(playlist.Path)) { - AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem"); + await AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem").ConfigureAwait(false); } if (item is IHasShares hasShares) { - AddShares(hasShares, writer); + await AddSharesAsync(hasShares, writer).ConfigureAwait(false); } - AddMediaInfo(item, writer); + await AddMediaInfo(item, writer).ConfigureAwait(false); } /// @@ -433,23 +411,26 @@ namespace MediaBrowser.LocalMetadata.Savers /// /// The item. /// The xml writer. - public static void AddShares(IHasShares item, XmlWriter writer) + /// The task object representing the asynchronous operation. + private static async Task AddSharesAsync(IHasShares item, XmlWriter writer) { - writer.WriteStartElement("Shares"); + await writer.WriteStartElementAsync(null, "Shares", null).ConfigureAwait(false); foreach (var share in item.Shares) { - writer.WriteStartElement("Share"); + await writer.WriteStartElementAsync(null, "Share", null).ConfigureAwait(false); - writer.WriteElementString("UserId", share.UserId); - writer.WriteElementString( + await writer.WriteElementStringAsync(null, "UserId", null, share.UserId).ConfigureAwait(false); + await writer.WriteElementStringAsync( + null, "CanEdit", - share.CanEdit.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); + null, + share.CanEdit.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()).ConfigureAwait(false); - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } /// @@ -458,33 +439,29 @@ namespace MediaBrowser.LocalMetadata.Savers /// The item. /// The xml writer. /// Type of item. - public static void AddMediaInfo(T item, XmlWriter writer) + /// The task object representing the asynchronous operation. + private static Task AddMediaInfo(T item, XmlWriter writer) where T : BaseItem { - if (item is Video video) + if (item is Video video && video.Video3DFormat.HasValue) { - if (video.Video3DFormat.HasValue) + return video.Video3DFormat switch { - switch (video.Video3DFormat.Value) - { - case Video3DFormat.FullSideBySide: - writer.WriteElementString("Format3D", "FSBS"); - break; - case Video3DFormat.FullTopAndBottom: - writer.WriteElementString("Format3D", "FTAB"); - break; - case Video3DFormat.HalfSideBySide: - writer.WriteElementString("Format3D", "HSBS"); - break; - case Video3DFormat.HalfTopAndBottom: - writer.WriteElementString("Format3D", "HTAB"); - break; - case Video3DFormat.MVC: - writer.WriteElementString("Format3D", "MVC"); - break; - } - } - } + Video3DFormat.FullSideBySide => + writer.WriteElementStringAsync(null, "Format3D", null, "FSBS"), + Video3DFormat.FullTopAndBottom => + writer.WriteElementStringAsync(null, "Format3D", null, "FTAB"), + Video3DFormat.HalfSideBySide => + writer.WriteElementStringAsync(null, "Format3D", null, "HSBS"), + Video3DFormat.HalfTopAndBottom => + writer.WriteElementStringAsync(null, "Format3D", null, "HTAB"), + Video3DFormat.MVC => + writer.WriteElementStringAsync(null, "Format3D", null, "MVC"), + _ => Task.CompletedTask + }; + } + + return Task.CompletedTask; } /// @@ -494,7 +471,8 @@ namespace MediaBrowser.LocalMetadata.Savers /// The xml writer. /// The plural node name. /// The singular node name. - public static void AddLinkedChildren(Folder item, XmlWriter writer, string pluralNodeName, string singularNodeName) + /// The task object representing the asynchronous operation. + private static async Task AddLinkedChildren(Folder item, XmlWriter writer, string pluralNodeName, string singularNodeName) { var items = item.LinkedChildren .Where(i => i.Type == LinkedChildType.Manual) @@ -505,28 +483,28 @@ namespace MediaBrowser.LocalMetadata.Savers return; } - writer.WriteStartElement(pluralNodeName); + await writer.WriteStartElementAsync(null, pluralNodeName, null).ConfigureAwait(false); foreach (var link in items) { if (!string.IsNullOrWhiteSpace(link.Path) || !string.IsNullOrWhiteSpace(link.LibraryItemId)) { - writer.WriteStartElement(singularNodeName); + await writer.WriteStartElementAsync(null, singularNodeName, null).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(link.Path)) { - writer.WriteElementString("Path", link.Path); + await writer.WriteElementStringAsync(null, "Path", null, link.Path).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(link.LibraryItemId)) { - writer.WriteElementString("ItemId", link.LibraryItemId); + await writer.WriteElementStringAsync(null, "ItemId", null, link.LibraryItemId).ConfigureAwait(false); } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } } - writer.WriteEndElement(); + await writer.WriteEndElementAsync().ConfigureAwait(false); } } } diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs index 8a5da95bf3..940f51dacf 100644 --- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Threading.Tasks; using System.Xml; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -38,9 +39,8 @@ namespace MediaBrowser.LocalMetadata.Savers } /// - protected override void WriteCustomElements(BaseItem item, XmlWriter writer) - { - } + protected override Task WriteCustomElementsAsync(BaseItem item, XmlWriter writer) + => Task.CompletedTask; /// protected override string GetLocalSavePath(BaseItem item) diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs index 76252bc090..847add07f7 100644 --- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Threading.Tasks; using System.Xml; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -43,14 +44,16 @@ namespace MediaBrowser.LocalMetadata.Savers } /// - protected override void WriteCustomElements(BaseItem item, XmlWriter writer) + protected override Task WriteCustomElementsAsync(BaseItem item, XmlWriter writer) { var game = (Playlist)item; - if (!string.IsNullOrEmpty(game.PlaylistMediaType)) + if (string.IsNullOrEmpty(game.PlaylistMediaType)) { - writer.WriteElementString("PlaylistMediaType", game.PlaylistMediaType); + return Task.CompletedTask; } + + return writer.WriteElementStringAsync(null, "PlaylistMediaType", null, game.PlaylistMediaType); } /// diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 5b1ec8041e..891790fa2d 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -677,8 +677,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (!string.Equals(text, newText, StringComparison.Ordinal)) { - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) - using (var writer = new StreamWriter(fileStream, encoding)) + var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); + var writer = new StreamWriter(fileStream, encoding); + await using (fileStream.ConfigureAwait(false)) + await using (writer.ConfigureAwait(false)) { await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 0385ce6a76..5281b37217 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -128,9 +128,7 @@ namespace MediaBrowser.Providers.Manager _metadataProviders = metadataProviders.ToArray(); _externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray(); - _savers = metadataSavers - .Where(i => i is not IConfigurableProvider configurable || configurable.IsEnabled) - .ToArray(); + _savers = metadataSavers.ToArray(); } /// @@ -653,16 +651,12 @@ namespace MediaBrowser.Providers.Manager } /// - public void SaveMetadata(BaseItem item, ItemUpdateType updateType) - { - SaveMetadata(item, updateType, _savers); - } + public Task SaveMetadataAsync(BaseItem item, ItemUpdateType updateType) + => SaveMetadataAsync(item, updateType, _savers); /// - public void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable savers) - { - SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparison.OrdinalIgnoreCase))); - } + public Task SaveMetadataAsync(BaseItem item, ItemUpdateType updateType, IEnumerable savers) + => SaveMetadataAsync(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparison.OrdinalIgnoreCase))); /// /// Saves the metadata. @@ -670,7 +664,7 @@ namespace MediaBrowser.Providers.Manager /// The item. /// Type of the update. /// The savers. - private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable savers) + private async Task SaveMetadataAsync(BaseItem item, ItemUpdateType updateType, IEnumerable savers) { var libraryOptions = _libraryManager.GetLibraryOptions(item); @@ -695,7 +689,7 @@ namespace MediaBrowser.Providers.Manager try { _libraryMonitor.ReportFileSystemChangeBeginning(path); - saver.Save(item, CancellationToken.None); + await saver.SaveAsync(item, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -710,7 +704,7 @@ namespace MediaBrowser.Providers.Manager { try { - saver.Save(item, CancellationToken.None); + await saver.SaveAsync(item, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 34019e5824..d0e229b23d 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -248,8 +248,11 @@ namespace MediaBrowser.Providers.Subtitles var fileOptions = AsyncFile.WriteOptions; fileOptions.Mode = FileMode.CreateNew; fileOptions.PreallocationSize = stream.Length; - using var fs = new FileStream(savePath, fileOptions); - await stream.CopyToAsync(fs).ConfigureAwait(false); + var fs = new FileStream(savePath, fileOptions); + await using (fs.ConfigureAwait(false)) + { + await stream.CopyToAsync(fs).ConfigureAwait(false); + } return; } diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs index 935ff5f59f..a6216ef305 100644 --- a/MediaBrowser.XbmcMetadata/EntryPoint.cs +++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs @@ -47,7 +47,7 @@ namespace MediaBrowser.XbmcMetadata { if (!string.IsNullOrWhiteSpace(_config.GetNfoConfiguration().UserId)) { - SaveMetadataForItem(e.Item, ItemUpdateType.MetadataDownload); + _ = SaveMetadataForItemAsync(e.Item, ItemUpdateType.MetadataDownload); } } } @@ -58,7 +58,7 @@ namespace MediaBrowser.XbmcMetadata _userDataManager.UserDataSaved -= OnUserDataSaved; } - private void SaveMetadataForItem(BaseItem item, ItemUpdateType updateReason) + private async Task SaveMetadataForItemAsync(BaseItem item, ItemUpdateType updateReason) { if (!item.IsFileProtocol || !item.SupportsLocalMetadata) { @@ -67,7 +67,7 @@ namespace MediaBrowser.XbmcMetadata try { - _providerManager.SaveMetadata(item, updateReason, new[] { BaseNfoSaver.SaverName }); + await _providerManager.SaveMetadataAsync(item, updateReason, new[] { BaseNfoSaver.SaverName }).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index d099813040..39bd87e96b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; using System.Xml; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; @@ -180,7 +181,7 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - public void Save(BaseItem item, CancellationToken cancellationToken) + public async Task SaveAsync(BaseItem item, CancellationToken cancellationToken) { var path = GetSavePath(item); @@ -192,11 +193,11 @@ namespace MediaBrowser.XbmcMetadata.Savers cancellationToken.ThrowIfCancellationRequested(); - SaveToFile(memoryStream, path); + await SaveToFileAsync(memoryStream, path).ConfigureAwait(false); } } - private void SaveToFile(Stream stream, string path) + private async Task SaveToFileAsync(Stream stream, string path) { var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"Provided path ({path}) is not valid.", nameof(path)); Directory.CreateDirectory(directory); @@ -209,12 +210,14 @@ namespace MediaBrowser.XbmcMetadata.Savers Mode = FileMode.Create, Access = FileAccess.Write, Share = FileShare.None, - PreallocationSize = stream.Length + PreallocationSize = stream.Length, + Options = FileOptions.Asynchronous }; - using (var filestream = new FileStream(path, fileStreamOptions)) + var filestream = new FileStream(path, fileStreamOptions); + await using (filestream.ConfigureAwait(false)) { - stream.CopyTo(filestream); + await stream.CopyToAsync(filestream).ConfigureAwait(false); } if (ConfigurationManager.Configuration.SaveMetadataHidden)