mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-04-24 14:08:44 -04:00
Merge pull request #6177 from Bond-009/async
Use async FileStreams where it makes sense
This commit is contained in:
commit
74fef6c05b
40 changed files with 125 additions and 98 deletions
|
@ -366,7 +366,7 @@ namespace Emby.Dlna
|
||||||
Directory.CreateDirectory(systemProfilesPath);
|
Directory.CreateDirectory(systemProfilesPath);
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
|
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace Emby.Drawing
|
||||||
{
|
{
|
||||||
var file = await ProcessImage(options).ConfigureAwait(false);
|
var file = await ProcessImage(options).ConfigureAwait(false);
|
||||||
|
|
||||||
using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true))
|
using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
||||||
{
|
{
|
||||||
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
|
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -815,7 +815,7 @@ namespace Emby.Server.Implementations.Channels
|
||||||
{
|
{
|
||||||
if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
|
if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
await using FileStream jsonStream = File.OpenRead(cachePath);
|
await using FileStream jsonStream = AsyncFile.OpenRead(cachePath);
|
||||||
var cachedResult = await JsonSerializer.DeserializeAsync<ChannelItemResult>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var cachedResult = await JsonSerializer.DeserializeAsync<ChannelItemResult>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
if (cachedResult != null)
|
if (cachedResult != null)
|
||||||
{
|
{
|
||||||
|
@ -838,7 +838,7 @@ namespace Emby.Server.Implementations.Channels
|
||||||
{
|
{
|
||||||
if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
|
if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
await using FileStream jsonStream = File.OpenRead(cachePath);
|
await using FileStream jsonStream = AsyncFile.OpenRead(cachePath);
|
||||||
var cachedResult = await JsonSerializer.DeserializeAsync<ChannelItemResult>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var cachedResult = await JsonSerializer.DeserializeAsync<ChannelItemResult>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
if (cachedResult != null)
|
if (cachedResult != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -248,7 +248,7 @@ namespace Emby.Server.Implementations.IO
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream thisFileStream = File.OpenRead(fileInfo.FullName))
|
using (Stream thisFileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 1))
|
||||||
{
|
{
|
||||||
result.Length = thisFileStream.Length;
|
result.Length = thisFileStream.Length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using FileStream jsonStream = File.OpenRead(cacheFilePath);
|
await using FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath);
|
||||||
mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// _logger.LogDebug("Found cached media info");
|
// _logger.LogDebug("Found cached media info");
|
||||||
|
@ -86,7 +87,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
if (cacheFilePath != null)
|
if (cacheFilePath != null)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||||
await using FileStream createStream = File.OpenWrite(cacheFilePath);
|
await using FileStream createStream = AsyncFile.OpenWrite(cacheFilePath);
|
||||||
await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(createStream, mediaInfo, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// _logger.LogDebug("Saved media info to {0}", cacheFilePath);
|
// _logger.LogDebug("Saved media info to {0}", cacheFilePath);
|
||||||
|
|
|
@ -638,7 +638,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using FileStream jsonStream = File.OpenRead(cacheFilePath);
|
await using FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath);
|
||||||
mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
mediaInfo = await JsonSerializer.DeserializeAsync<MediaInfo>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// _logger.LogDebug("Found cached media info");
|
// _logger.LogDebug("Found cached media info");
|
||||||
|
|
|
@ -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)));
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
||||||
{
|
{
|
||||||
onStarted();
|
onStarted();
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None);
|
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, AsyncFile.UseAsyncIO);
|
||||||
|
|
||||||
onStarted();
|
onStarted();
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
||||||
|
|
||||||
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||||
_logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
_logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
|
|
||||||
await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
|
await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
|
|
||||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(path, cancellationToken).ConfigureAwait(false);
|
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(path, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew))
|
await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, AsyncFile.UseAsyncIO))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(channelCacheFile));
|
Directory.CreateDirectory(Path.GetDirectoryName(channelCacheFile));
|
||||||
await using var writeStream = File.OpenWrite(channelCacheFile);
|
await using var writeStream = AsyncFile.OpenWrite(channelCacheFile);
|
||||||
await JsonSerializer.SerializeAsync(writeStream, channels, cancellationToken: cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(writeStream, channels, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
|
@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using var readStream = File.OpenRead(channelCacheFile);
|
await using var readStream = AsyncFile.OpenRead(channelCacheFile);
|
||||||
var channels = await JsonSerializer.DeserializeAsync<List<ChannelInfo>>(readStream, cancellationToken: cancellationToken)
|
var channels = await JsonSerializer.DeserializeAsync<List<ChannelInfo>>(readStream, cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
list.AddRange(channels);
|
list.AddRange(channels);
|
||||||
|
|
|
@ -155,15 +155,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token);
|
using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token);
|
||||||
cancellationToken = linkedCancellationTokenSource.Token;
|
cancellationToken = linkedCancellationTokenSource.Token;
|
||||||
|
|
||||||
// use non-async filestream on windows along with read due to https://github.com/dotnet/corefx/issues/6039
|
|
||||||
var allowAsync = Environment.OSVersion.Platform != PlatformID.Win32NT;
|
|
||||||
|
|
||||||
bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
|
bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
|
||||||
|
|
||||||
var nextFileInfo = GetNextFile(null);
|
var nextFileInfo = GetNextFile(null);
|
||||||
var nextFile = nextFileInfo.file;
|
var nextFile = nextFileInfo.file;
|
||||||
var isLastFile = nextFileInfo.isLastFile;
|
var isLastFile = nextFileInfo.isLastFile;
|
||||||
|
|
||||||
|
var allowAsync = AsyncFile.UseAsyncIO;
|
||||||
while (!string.IsNullOrEmpty(nextFile))
|
while (!string.IsNullOrEmpty(nextFile))
|
||||||
{
|
{
|
||||||
var emptyReadLimit = isLastFile ? EmptyReadLimit : 1;
|
var emptyReadLimit = isLastFile ? EmptyReadLimit : 1;
|
||||||
|
|
|
@ -14,6 +14,7 @@ using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return File.OpenRead(info.Url);
|
return AsyncFile.OpenRead(info.Url);
|
||||||
}
|
}
|
||||||
|
|
||||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
||||||
|
|
|
@ -129,37 +129,39 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
private Task StartStreaming(HttpResponseMessage response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
private Task StartStreaming(HttpResponseMessage response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.Run(async () =>
|
return Task.Run(
|
||||||
{
|
async () =>
|
||||||
try
|
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath);
|
try
|
||||||
using var message = response;
|
{
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath);
|
||||||
await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
|
using var message = response;
|
||||||
await StreamHelper.CopyToAsync(
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
stream,
|
await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
fileStream,
|
await StreamHelper.CopyToAsync(
|
||||||
IODefaults.CopyToBufferSize,
|
stream,
|
||||||
() => Resolve(openTaskCompletionSource),
|
fileStream,
|
||||||
cancellationToken).ConfigureAwait(false);
|
IODefaults.CopyToBufferSize,
|
||||||
}
|
() => Resolve(openTaskCompletionSource),
|
||||||
catch (OperationCanceledException ex)
|
cancellationToken).ConfigureAwait(false);
|
||||||
{
|
}
|
||||||
Logger.LogInformation("Copying of {0} to {1} was canceled", GetType().Name, TempFilePath);
|
catch (OperationCanceledException ex)
|
||||||
openTaskCompletionSource.TrySetException(ex);
|
{
|
||||||
}
|
Logger.LogInformation("Copying of {0} to {1} was canceled", GetType().Name, TempFilePath);
|
||||||
catch (Exception ex)
|
openTaskCompletionSource.TrySetException(ex);
|
||||||
{
|
}
|
||||||
Logger.LogError(ex, "Error copying live stream {0} to {1}.", GetType().Name, TempFilePath);
|
catch (Exception ex)
|
||||||
openTaskCompletionSource.TrySetException(ex);
|
{
|
||||||
}
|
Logger.LogError(ex, "Error copying live stream {0} to {1}.", GetType().Name, TempFilePath);
|
||||||
|
openTaskCompletionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
openTaskCompletionSource.TrySetResult(false);
|
openTaskCompletionSource.TrySetResult(false);
|
||||||
|
|
||||||
EnableStreamSharing = false;
|
EnableStreamSharing = false;
|
||||||
await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
|
await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
|
||||||
}, CancellationToken.None);
|
},
|
||||||
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
|
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
|
||||||
|
|
|
@ -15,6 +15,7 @@ using Jellyfin.Extensions.Json.Converters;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Plugins;
|
using MediaBrowser.Model.Plugins;
|
||||||
using MediaBrowser.Model.Updates;
|
using MediaBrowser.Model.Updates;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
@ -371,7 +372,7 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
var url = new Uri(packageInfo.ImageUrl);
|
var url = new Uri(packageInfo.ImageUrl);
|
||||||
imagePath = Path.Join(path, url.Segments[^1]);
|
imagePath = Path.Join(path, url.Segments[^1]);
|
||||||
|
|
||||||
await using var fileStream = File.OpenWrite(imagePath);
|
await using var fileStream = AsyncFile.OpenWrite(imagePath);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.Serialization
|
||||||
/// <param name="file">The file.</param>
|
/// <param name="file">The file.</param>
|
||||||
public void SerializeToFile(object obj, string file)
|
public void SerializeToFile(object obj, string file)
|
||||||
{
|
{
|
||||||
using (var stream = new FileStream(file, FileMode.Create))
|
using (var stream = new FileStream(file, FileMode.Create, FileAccess.Write))
|
||||||
{
|
{
|
||||||
SerializeToStream(obj, stream);
|
SerializeToStream(obj, stream);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentType = MimeTypes.GetMimeType(path);
|
var contentType = MimeTypes.GetMimeType(path);
|
||||||
return File(System.IO.File.OpenRead(path), contentType);
|
return File(AsyncFile.OpenRead(path), contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -208,7 +208,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
|
var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
|
||||||
Directory.CreateDirectory(fullCacheDirectory);
|
Directory.CreateDirectory(fullCacheDirectory);
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true);
|
await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
|
|
||||||
var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath));
|
var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath));
|
||||||
|
|
|
@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
// For older files, assume fully static
|
// For older files, assume fully static
|
||||||
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
|
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
|
||||||
FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare);
|
FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
return File(stream, "text/plain; charset=utf-8");
|
return File(stream, "text/plain; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
FileAccess.Read,
|
FileAccess.Read,
|
||||||
FileShare.ReadWrite,
|
FileShare.ReadWrite,
|
||||||
IODefaults.FileStreamBufferSize,
|
IODefaults.FileStreamBufferSize,
|
||||||
FileOptions.SequentialScan);
|
(AsyncFile.UseAsyncIO ? FileOptions.Asynchronous : FileOptions.None) | FileOptions.SequentialScan);
|
||||||
await using (fileStream.ConfigureAwait(false))
|
await using (fileStream.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
using var reader = new StreamReader(fileStream);
|
using var reader = new StreamReader(fileStream);
|
||||||
|
|
|
@ -85,8 +85,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
var fileOptions = FileOptions.SequentialScan;
|
var fileOptions = FileOptions.SequentialScan;
|
||||||
var allowAsyncFileRead = false;
|
var allowAsyncFileRead = false;
|
||||||
|
|
||||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
if (AsyncFile.UseAsyncIO)
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
{
|
||||||
fileOptions |= FileOptions.Asynchronous;
|
fileOptions |= FileOptions.Asynchronous;
|
||||||
allowAsyncFileRead = true;
|
allowAsyncFileRead = true;
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
_allowAsyncFileRead = false;
|
_allowAsyncFileRead = false;
|
||||||
|
|
||||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (AsyncFile.UseAsyncIO)
|
||||||
{
|
{
|
||||||
fileOptions |= FileOptions.Asynchronous;
|
fileOptions |= FileOptions.Asynchronous;
|
||||||
_allowAsyncFileRead = true;
|
_allowAsyncFileRead = true;
|
||||||
|
|
|
@ -557,7 +557,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
$"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
|
$"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
|
||||||
|
|
||||||
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||||
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
|
|
||||||
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
||||||
await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
|
await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
|
|
@ -10,6 +10,7 @@ using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.Users
|
namespace Jellyfin.Server.Implementations.Users
|
||||||
|
@ -53,7 +54,7 @@ namespace Jellyfin.Server.Implementations.Users
|
||||||
foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
|
foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
|
||||||
{
|
{
|
||||||
SerializablePasswordReset spr;
|
SerializablePasswordReset spr;
|
||||||
await using (var str = File.OpenRead(resetFile))
|
await using (var str = AsyncFile.OpenRead(resetFile))
|
||||||
{
|
{
|
||||||
spr = await JsonSerializer.DeserializeAsync<SerializablePasswordReset>(str).ConfigureAwait(false)
|
spr = await JsonSerializer.DeserializeAsync<SerializablePasswordReset>(str).ConfigureAwait(false)
|
||||||
?? throw new ResourceNotFoundException($"Provided path ({resetFile}) is not valid.");
|
?? throw new ResourceNotFoundException($"Provided path ({resetFile}) is not valid.");
|
||||||
|
@ -110,7 +111,7 @@ namespace Jellyfin.Server.Implementations.Users
|
||||||
UserName = user.Username
|
UserName = user.Username
|
||||||
};
|
};
|
||||||
|
|
||||||
await using (FileStream fileStream = File.OpenWrite(filePath))
|
await using (FileStream fileStream = AsyncFile.OpenWrite(filePath))
|
||||||
{
|
{
|
||||||
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false);
|
||||||
await fileStream.FlushAsync().ConfigureAwait(false);
|
await fileStream.FlushAsync().ConfigureAwait(false);
|
||||||
|
|
|
@ -15,6 +15,7 @@ using Jellyfin.Server.Implementations;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Extensions;
|
using MediaBrowser.Controller.Extensions;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
@ -546,7 +547,7 @@ namespace Jellyfin.Server
|
||||||
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
|
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
|
||||||
|
|
||||||
// Copy the resource contents to the expected file path for the config file
|
// Copy the resource contents to the expected file path for the config file
|
||||||
await using Stream dst = File.Open(configPath, FileMode.CreateNew);
|
await using Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await resource.CopyToAsync(dst).ConfigureAwait(false);
|
await resource.CopyToAsync(dst).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource, mediaAttachment, cancellationToken).ConfigureAwait(false);
|
var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource, mediaAttachment, cancellationToken).ConfigureAwait(false);
|
||||||
return File.OpenRead(attachmentPath);
|
return AsyncFile.OpenRead(attachmentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetReadableFile(
|
private async Task<string> GetReadableFile(
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo
|
||||||
|
|
||||||
public bool IsDir => _impl.IsDirectory;
|
public bool IsDir => _impl.IsDirectory;
|
||||||
|
|
||||||
public System.IO.Stream OpenRead()
|
public Stream OpenRead()
|
||||||
{
|
{
|
||||||
return new FileStream(
|
return new FileStream(
|
||||||
FullName,
|
FullName,
|
||||||
|
@ -33,9 +33,9 @@ namespace MediaBrowser.MediaEncoding.BdInfo
|
||||||
FileShare.Read);
|
FileShare.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
public System.IO.StreamReader OpenText()
|
public StreamReader OpenText()
|
||||||
{
|
{
|
||||||
return new System.IO.StreamReader(OpenRead());
|
return new StreamReader(OpenRead());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1552,7 +1552,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||||
{
|
{
|
||||||
var packetBuffer = new byte[197];
|
var packetBuffer = new byte[197];
|
||||||
|
|
||||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1))
|
||||||
{
|
{
|
||||||
fs.Read(packetBuffer);
|
fs.Read(packetBuffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return File.OpenRead(fileInfo.Path);
|
return AsyncFile.OpenRead(fileInfo.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SubtitleInfo> GetReadableFile(
|
private async Task<SubtitleInfo> GetReadableFile(
|
||||||
|
@ -671,7 +671,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
string text;
|
string text;
|
||||||
Encoding encoding;
|
Encoding encoding;
|
||||||
|
|
||||||
using (var fileStream = File.OpenRead(file))
|
using (var fileStream = AsyncFile.OpenRead(file))
|
||||||
using (var reader = new StreamReader(fileStream, true))
|
using (var reader = new StreamReader(fileStream, true))
|
||||||
{
|
{
|
||||||
encoding = reader.CurrentEncoding;
|
encoding = reader.CurrentEncoding;
|
||||||
|
@ -684,7 +684,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
if (!string.Equals(text, newText, StringComparison.Ordinal))
|
if (!string.Equals(text, newText, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
|
using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
||||||
using (var writer = new StreamWriter(fileStream, encoding))
|
using (var writer = new StreamWriter(fileStream, encoding))
|
||||||
{
|
{
|
||||||
await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false);
|
await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false);
|
||||||
|
@ -750,7 +750,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
}
|
}
|
||||||
|
|
||||||
case MediaProtocol.File:
|
case MediaProtocol.File:
|
||||||
return File.OpenRead(path);
|
return AsyncFile.OpenRead(path);
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(protocol));
|
throw new ArgumentOutOfRangeException(nameof(protocol));
|
||||||
}
|
}
|
||||||
|
|
34
MediaBrowser.Model/IO/AsyncFile.cs
Normal file
34
MediaBrowser.Model/IO/AsyncFile.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Model.IO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class to create async <see cref="FileStream" />s.
|
||||||
|
/// </summary>
|
||||||
|
public static class AsyncFile
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether we should use async IO on this platform.
|
||||||
|
/// <see href="https://github.com/dotnet/runtime/issues/16354" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns <c>false</c> on Windows; otherwise <c>true</c>.</returns>
|
||||||
|
public static bool UseAsyncIO => !OperatingSystem.IsWindows();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens an existing file for reading.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
|
/// <returns>A read-only <see cref="FileStream" /> on the specified path.</returns>
|
||||||
|
public static FileStream OpenRead(string path)
|
||||||
|
=> new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, UseAsyncIO);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens an existing file for writing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
|
/// <returns>An unshared <see cref="FileStream" /> object on the specified path with Write access.</returns>
|
||||||
|
public static FileStream OpenWrite(string path)
|
||||||
|
=> new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, UseAsyncIO);
|
||||||
|
}
|
||||||
|
}
|
|
@ -264,7 +264,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
_fileSystem.SetAttributes(path, false, false);
|
_fileSystem.SetAttributes(path, false, false);
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
||||||
{
|
{
|
||||||
await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
|
await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
{
|
{
|
||||||
var mimeType = MimeTypes.GetMimeType(response.Path);
|
var mimeType = MimeTypes.GetMimeType(response.Path);
|
||||||
|
|
||||||
var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
|
|
||||||
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
|
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
throw new ArgumentNullException(nameof(source));
|
throw new ArgumentNullException(nameof(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true);
|
var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
|
||||||
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
|
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Providers;
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.AudioDb
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
|
||||||
var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id);
|
var path = AudioDbAlbumProvider.GetAlbumInfoPath(_config.ApplicationPaths, id);
|
||||||
|
|
||||||
await using FileStream jsonStream = File.OpenRead(path);
|
await using FileStream jsonStream = AsyncFile.OpenRead(path);
|
||||||
var obj = await JsonSerializer.DeserializeAsync<AudioDbAlbumProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var obj = await JsonSerializer.DeserializeAsync<AudioDbAlbumProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (obj != null && obj.album != null && obj.album.Count > 0)
|
if (obj != null && obj.album != null && obj.album.Count > 0)
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
|
||||||
var path = GetAlbumInfoPath(_config.ApplicationPaths, id);
|
var path = GetAlbumInfoPath(_config.ApplicationPaths, id);
|
||||||
|
|
||||||
await using FileStream jsonStream = File.OpenRead(path);
|
await using FileStream jsonStream = AsyncFile.OpenRead(path);
|
||||||
var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (obj != null && obj.album != null && obj.album.Count > 0)
|
if (obj != null && obj.album != null && obj.album.Count > 0)
|
||||||
|
@ -173,7 +173,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
|
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true);
|
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Providers;
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.AudioDb
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
@ -59,7 +60,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
|
||||||
var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id);
|
var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id);
|
||||||
|
|
||||||
await using FileStream jsonStream = File.OpenRead(path);
|
await using FileStream jsonStream = AsyncFile.OpenRead(path);
|
||||||
var obj = await JsonSerializer.DeserializeAsync<AudioDbArtistProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var obj = await JsonSerializer.DeserializeAsync<AudioDbArtistProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (obj != null && obj.artists != null && obj.artists.Count > 0)
|
if (obj != null && obj.artists != null && obj.artists.Count > 0)
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
|
|
||||||
var path = GetArtistInfoPath(_config.ApplicationPaths, id);
|
var path = GetArtistInfoPath(_config.ApplicationPaths, id);
|
||||||
|
|
||||||
await using FileStream jsonStream = File.OpenRead(path);
|
await using FileStream jsonStream = AsyncFile.OpenRead(path);
|
||||||
var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (obj != null && obj.artists != null && obj.artists.Count > 0)
|
if (obj != null && obj.artists != null && obj.artists.Count > 0)
|
||||||
|
@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, true);
|
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,31 +236,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
|
internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var path = await EnsureItemInfo(imdbId, cancellationToken).ConfigureAwait(false);
|
var path = await EnsureItemInfo(imdbId, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = File.OpenRead(path);
|
await using var stream = AsyncFile.OpenRead(path);
|
||||||
return await JsonSerializer.DeserializeAsync<RootObject>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
return await JsonSerializer.DeserializeAsync<RootObject>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<SeasonRootObject> GetSeasonRootObject(string imdbId, int seasonId, CancellationToken cancellationToken)
|
internal async Task<SeasonRootObject> GetSeasonRootObject(string imdbId, int seasonId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var path = await EnsureSeasonInfo(imdbId, seasonId, cancellationToken).ConfigureAwait(false);
|
var path = await EnsureSeasonInfo(imdbId, seasonId, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = File.OpenRead(path);
|
await using var stream = AsyncFile.OpenRead(path);
|
||||||
return await JsonSerializer.DeserializeAsync<SeasonRootObject>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
return await JsonSerializer.DeserializeAsync<SeasonRootObject>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
|
|
||||||
{
|
|
||||||
if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string id))
|
|
||||||
{
|
|
||||||
// This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet.
|
|
||||||
if (!string.IsNullOrWhiteSpace(id))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Gets OMDB URL.</summary>
|
/// <summary>Gets OMDB URL.</summary>
|
||||||
/// <param name="query">Appends query string to URL.</param>
|
/// <param name="query">Appends query string to URL.</param>
|
||||||
/// <returns>OMDB URL with optional query string.</returns>
|
/// <returns>OMDB URL with optional query string.</returns>
|
||||||
|
@ -309,7 +295,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
imdbParam));
|
imdbParam));
|
||||||
|
|
||||||
var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
||||||
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
|
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
@ -349,7 +335,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
seasonId));
|
seasonId));
|
||||||
|
|
||||||
var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
||||||
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
|
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
|
|
@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.Studios
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||||
await using var response = await httpClient.GetStreamAsync(url, cancellationToken).ConfigureAwait(false);
|
await using var response = await httpClient.GetStreamAsync(url, cancellationToken).ConfigureAwait(false);
|
||||||
await using var fileStream = new FileStream(file, FileMode.Create);
|
await using var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -245,7 +245,7 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, true);
|
using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
||||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Extensions.Json;
|
using Jellyfin.Extensions.Json;
|
||||||
using MediaBrowser.MediaEncoding.Probing;
|
using MediaBrowser.MediaEncoding.Probing;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Jellyfin.MediaEncoding.Tests
|
namespace Jellyfin.MediaEncoding.Tests
|
||||||
|
@ -14,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Tests
|
||||||
public async Task Test(string fileName)
|
public async Task Test(string fileName)
|
||||||
{
|
{
|
||||||
var path = Path.Join("Test Data", fileName);
|
var path = Path.Join("Test Data", fileName);
|
||||||
await using (var stream = File.OpenRead(path))
|
await using (var stream = AsyncFile.OpenRead(path))
|
||||||
{
|
{
|
||||||
var res = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false);
|
var res = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false);
|
||||||
Assert.NotNull(res);
|
Assert.NotNull(res);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue