diff options
Diffstat (limited to 'Jellyfin.Api/Helpers')
| -rw-r--r-- | Jellyfin.Api/Helpers/ProgressiveFileCopier.cs | 84 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/RequestHelpers.cs | 70 |
2 files changed, 148 insertions, 6 deletions
diff --git a/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs b/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs new file mode 100644 index 000000000..e8e6966f4 --- /dev/null +++ b/Jellyfin.Api/Helpers/ProgressiveFileCopier.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.IO; + +namespace Jellyfin.Api.Helpers +{ + /// <summary> + /// Progressive file copier. + /// </summary> + public class ProgressiveFileCopier + { + private readonly string? _path; + private readonly IDirectStreamProvider? _directStreamProvider; + private readonly IStreamHelper _streamHelper; + + /// <summary> + /// Initializes a new instance of the <see cref="ProgressiveFileCopier"/> class. + /// </summary> + /// <param name="streamHelper">Instance of the <see cref="IStreamHelper"/> interface.</param> + /// <param name="path">Filepath to stream from.</param> + public ProgressiveFileCopier(IStreamHelper streamHelper, string path) + { + _path = path; + _streamHelper = streamHelper; + _directStreamProvider = null; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ProgressiveFileCopier"/> class. + /// </summary> + /// <param name="streamHelper">Instance of the <see cref="IStreamHelper"/> interface.</param> + /// <param name="directStreamProvider">Instance of the <see cref="IDirectStreamProvider"/> interface.</param> + public ProgressiveFileCopier(IStreamHelper streamHelper, IDirectStreamProvider directStreamProvider) + { + _directStreamProvider = directStreamProvider; + _streamHelper = streamHelper; + _path = null; + } + + /// <summary> + /// Write source stream to output. + /// </summary> + /// <param name="outputStream">Output stream.</param> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>A <see cref="Task"/>.</returns> + public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) + { + if (_directStreamProvider != null) + { + await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false); + return; + } + + var fileOptions = FileOptions.SequentialScan; + + // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + { + fileOptions |= FileOptions.Asynchronous; + } + + await using var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, fileOptions); + const int emptyReadLimit = 100; + var eofCount = 0; + while (eofCount < emptyReadLimit) + { + var bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); + + if (bytesRead == 0) + { + eofCount++; + await Task.Delay(100, cancellationToken).ConfigureAwait(false); + } + else + { + eofCount = 0; + } + } + } + } +} diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index e2a0cf4fa..ce29b90c6 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -3,6 +3,8 @@ using System.Linq; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Http; namespace Jellyfin.Api.Helpers @@ -19,7 +21,7 @@ namespace Jellyfin.Api.Helpers /// <param name="separator">The char that separates the substrings.</param> /// <param name="removeEmpty">Option to remove empty substrings from the array.</param> /// <returns>An array of the substrings.</returns> - internal static string[] Split(string value, char separator, bool removeEmpty) + internal static string[] Split(string? value, char separator, bool removeEmpty) { if (string.IsNullOrWhiteSpace(value)) { @@ -76,20 +78,76 @@ namespace Jellyfin.Api.Helpers } /// <summary> - /// Get Guid array from string. + /// Gets the item fields. /// </summary> - /// <param name="value">String value.</param> - /// <returns>Guid array.</returns> + /// <param name="fields">The item field string.</param> + /// <returns>Array of parsed item fields.</returns> + internal static ItemFields[] GetItemFields(string fields) + { + if (string.IsNullOrEmpty(fields)) + { + return Array.Empty<ItemFields>(); + } + + return Split(fields, ',', true) + .Select(v => + { + if (Enum.TryParse(v, true, out ItemFields value)) + { + return (ItemFields?)value; + } + + return null; + }) + .Where(i => i.HasValue) + .Select(i => i!.Value) + .ToArray(); + } + internal static Guid[] GetGuids(string? value) { - if (value == null) + if (string.IsNullOrEmpty(value)) { return Array.Empty<Guid>(); } - return value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + return Split(value, ',', true) .Select(i => new Guid(i)) .ToArray(); } + + internal static ValueTuple<string, SortOrder>[] GetOrderBy(string? sortBy, string? requestedSortOrder) + { + var val = sortBy; + + if (string.IsNullOrEmpty(val)) + { + return Array.Empty<ValueTuple<string, SortOrder>>(); + } + + var vals = val.Split(','); + if (string.IsNullOrWhiteSpace(requestedSortOrder)) + { + requestedSortOrder = "Ascending"; + } + + var sortOrders = requestedSortOrder.Split(','); + + var result = new ValueTuple<string, SortOrder>[vals.Length]; + + for (var i = 0; i < vals.Length; i++) + { + var sortOrderIndex = sortOrders.Length > i ? i : 0; + + var sortOrderValue = sortOrders.Length > sortOrderIndex ? sortOrders[sortOrderIndex] : null; + var sortOrder = string.Equals(sortOrderValue, "Descending", StringComparison.OrdinalIgnoreCase) + ? SortOrder.Descending + : SortOrder.Ascending; + + result[i] = new ValueTuple<string, SortOrder>(vals[i], sortOrder); + } + + return result; + } } } |
