diff options
Diffstat (limited to 'Jellyfin.Api/Helpers')
| -rw-r--r-- | Jellyfin.Api/Helpers/ClassMigrationHelper.cs | 71 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 7 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/ProgressiveFileStream.cs | 76 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/RequestHelpers.cs | 16 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/StreamingHelpers.cs | 1 |
5 files changed, 54 insertions, 117 deletions
diff --git a/Jellyfin.Api/Helpers/ClassMigrationHelper.cs b/Jellyfin.Api/Helpers/ClassMigrationHelper.cs deleted file mode 100644 index 76fb27bcc..000000000 --- a/Jellyfin.Api/Helpers/ClassMigrationHelper.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Reflection; - -namespace Jellyfin.Api.Helpers -{ - /// <summary> - /// A static class for copying matching properties from one object to another. - /// TODO: remove at the point when a fixed migration path has been decided upon. - /// </summary> - public static class ClassMigrationHelper - { - /// <summary> - /// Extension for 'Object' that copies the properties to a destination object. - /// </summary> - /// <param name="source">The source.</param> - /// <param name="destination">The destination.</param> - public static void CopyProperties(this object source, object destination) - { - // If any this null throw an exception. - if (source == null || destination == null) - { - throw new ArgumentException("Source or/and Destination Objects are null"); - } - - // Getting the Types of the objects. - Type typeDest = destination.GetType(); - Type typeSrc = source.GetType(); - - // Iterate the Properties of the source instance and populate them from their destination counterparts. - PropertyInfo[] srcProps = typeSrc.GetProperties(); - foreach (PropertyInfo srcProp in srcProps) - { - if (!srcProp.CanRead) - { - continue; - } - - var targetProperty = typeDest.GetProperty(srcProp.Name); - if (targetProperty == null) - { - continue; - } - - if (!targetProperty.CanWrite) - { - continue; - } - - var obj = targetProperty.GetSetMethod(true); - if (obj != null && obj.IsPrivate) - { - continue; - } - - var target = targetProperty.GetSetMethod(); - if (target != null && (target.Attributes & MethodAttributes.Static) != 0) - { - continue; - } - - if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)) - { - continue; - } - - // Passed all tests, lets set the value. - targetProperty.SetValue(destination, srcProp.GetValue(source, null), null); - } - } - } -} diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 4abe4c5d5..02af2e435 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -462,6 +462,11 @@ namespace Jellyfin.Api.Helpers private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder, ClaimsPrincipal user) { + if (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Drop) + { + return; + } + var selectedIndex = state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index; const string Format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\""; diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs index 61e18220a..3fa07720a 100644 --- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs +++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs @@ -17,7 +17,6 @@ namespace Jellyfin.Api.Helpers private readonly TranscodingJobDto? _job; private readonly TranscodingJobHelper? _transcodingJobHelper; private readonly int _timeoutMs; - private int _bytesWritten; private bool _disposed; /// <summary> @@ -71,53 +70,58 @@ namespace Jellyfin.Api.Helpers /// <inheritdoc /> public override void Flush() { - _stream.Flush(); + // Not supported } /// <inheritdoc /> public override int Read(byte[] buffer, int offset, int count) + => Read(buffer.AsSpan(offset, count)); + + /// <inheritdoc /> + public override int Read(Span<byte> buffer) { - return _stream.Read(buffer, offset, count); + int totalBytesRead = 0; + var stopwatch = Stopwatch.StartNew(); + + while (KeepReading(stopwatch.ElapsedMilliseconds)) + { + totalBytesRead += _stream.Read(buffer); + if (totalBytesRead > 0) + { + break; + } + + Thread.Sleep(50); + } + + UpdateBytesWritten(totalBytesRead); + + return totalBytesRead; } /// <inheritdoc /> public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => await ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false); + + /// <inheritdoc /> + public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) { int totalBytesRead = 0; - int remainingBytesToRead = count; var stopwatch = Stopwatch.StartNew(); - int newOffset = offset; - while (remainingBytesToRead > 0) + while (KeepReading(stopwatch.ElapsedMilliseconds)) { - cancellationToken.ThrowIfCancellationRequested(); - int bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false); - - remainingBytesToRead -= bytesRead; - newOffset += bytesRead; - - if (bytesRead > 0) + totalBytesRead += await _stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + if (totalBytesRead > 0) { - _bytesWritten += bytesRead; - totalBytesRead += bytesRead; - - if (_job != null) - { - _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten); - } + break; } - else - { - // If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely - if (_job?.HasExited ?? stopwatch.ElapsedMilliseconds > _timeoutMs) - { - break; - } - await Task.Delay(50, cancellationToken).ConfigureAwait(false); - } + await Task.Delay(50, cancellationToken).ConfigureAwait(false); } + UpdateBytesWritten(totalBytesRead); + return totalBytesRead; } @@ -159,5 +163,19 @@ namespace Jellyfin.Api.Helpers base.Dispose(disposing); } } + + private void UpdateBytesWritten(int totalBytesRead) + { + if (_job != null) + { + _job.BytesDownloaded += totalBytesRead; + } + } + + private bool KeepReading(long elapsed) + { + // If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely + return !_job?.HasExited ?? elapsed < _timeoutMs; + } } } diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index 0efd3443b..ca8bc0bdd 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -137,21 +137,5 @@ namespace Jellyfin.Api.Helpers TotalRecordCount = result.TotalRecordCount }; } - - internal static string[] GetItemTypeStrings(IReadOnlyList<BaseItemKind> itemKinds) - { - if (itemKinds.Count == 0) - { - return Array.Empty<string>(); - } - - var itemTypes = new string[itemKinds.Count]; - for (var i = 0; i < itemKinds.Count; i++) - { - itemTypes[i] = itemKinds[i].ToString(); - } - - return itemTypes; - } } } diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 1b8f24c27..ed071bcd7 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -90,6 +90,7 @@ namespace Jellyfin.Api.Helpers } var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) || + streamingRequest.StreamOptions.ContainsKey("dlnaheaders") || string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase); var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper) |
