David 4 лет назад
Родитель
Сommit
ca3dcc3db0

+ 28 - 79
Jellyfin.Api/Controllers/AudioController.cs

@@ -260,7 +260,7 @@ namespace Jellyfin.Api.Controllers
                 StreamOptions = streamOptions
             };
 
-            var state = await StreamingHelpers.GetStreamingState(
+            using var state = await StreamingHelpers.GetStreamingState(
                     streamingRequest,
                     Request,
                     _authContext,
@@ -283,14 +283,11 @@ namespace Jellyfin.Api.Controllers
             {
                 StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
 
-                using (state)
-                {
-                    // TODO AllowEndOfFile = false
-                    await new ProgressiveFileCopier(_streamHelper, state.DirectStreamProvider).WriteToAsync(Response.Body, CancellationToken.None).ConfigureAwait(false);
+                // TODO AllowEndOfFile = false
+                await new ProgressiveFileCopier(_streamHelper, state.DirectStreamProvider).WriteToAsync(Response.Body, CancellationToken.None).ConfigureAwait(false);
 
-                    // TODO (moved from MediaBrowser.Api): Don't hardcode contentType
-                    return File(Response.Body, MimeTypes.GetMimeType("file.ts")!);
-                }
+                // TODO (moved from MediaBrowser.Api): Don't hardcode contentType
+                return File(Response.Body, MimeTypes.GetMimeType("file.ts")!);
             }
 
             // Static remote stream
@@ -298,10 +295,7 @@ namespace Jellyfin.Api.Controllers
             {
                 StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
 
-                using (state)
-                {
-                    return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, _httpClient).ConfigureAwait(false);
-                }
+                return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, _httpClient).ConfigureAwait(false);
             }
 
             if (@static.HasValue && @static.Value && state.InputProtocol != MediaProtocol.File)
@@ -322,80 +316,35 @@ namespace Jellyfin.Api.Controllers
             {
                 var contentType = state.GetMimeType("." + state.OutputContainer, false) ?? state.GetMimeType(state.MediaPath);
 
-                using (state)
+                if (state.MediaSource.IsInfiniteStream)
                 {
-                    if (state.MediaSource.IsInfiniteStream)
-                    {
-                        // TODO AllowEndOfFile = false
-                        await new ProgressiveFileCopier(_streamHelper, state.MediaPath).WriteToAsync(Response.Body, CancellationToken.None).ConfigureAwait(false);
-
-                        return File(Response.Body, contentType);
-                    }
+                    // TODO AllowEndOfFile = false
+                    await new ProgressiveFileCopier(_streamHelper, state.MediaPath).WriteToAsync(Response.Body, CancellationToken.None).ConfigureAwait(false);
 
-                    return FileStreamResponseHelpers.GetStaticFileResult(
-                        state.MediaPath,
-                        contentType,
-                        isHeadRequest,
-                        this);
+                    return File(Response.Body, contentType);
                 }
-            }
 
-            /*
-            // Not static but transcode cache file exists
-            if (isTranscodeCached && state.VideoRequest == null)
-            {
-                var contentType = state.GetMimeType(outputPath)
-                try
-                {
-                    if (transcodingJob != null)
-                    {
-                        ApiEntryPoint.Instance.OnTranscodeBeginRequest(transcodingJob);
-                    }
-                    return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
-                    {
-                        ResponseHeaders = responseHeaders,
-                        ContentType = contentType,
-                        IsHeadRequest = isHeadRequest,
-                        Path = outputPath,
-                        FileShare = FileShare.ReadWrite,
-                        OnComplete = () =>
-                        {
-                            if (transcodingJob != null)
-                            {
-                                ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
-                            }
-                    }).ConfigureAwait(false);
-                }
-                finally
-                {
-                    state.Dispose();
-                }
-            }
-            */
-
-            // Need to start ffmpeg (because media can't be returned directly)
-            try
-            {
-                var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
-                var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
-                var ffmpegCommandLineArguments = encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
-                return await FileStreamResponseHelpers.GetTranscodedFile(
-                    state,
+                return FileStreamResponseHelpers.GetStaticFileResult(
+                    state.MediaPath,
+                    contentType,
                     isHeadRequest,
-                    _streamHelper,
-                    this,
-                    _transcodingJobHelper,
-                    ffmpegCommandLineArguments,
-                    Request,
-                    _transcodingJobType,
-                    cancellationTokenSource).ConfigureAwait(false);
+                    this);
             }
-            catch
-            {
-                state.Dispose();
 
-                throw;
-            }
+            // Need to start ffmpeg (because media can't be returned directly)
+            var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
+            var encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
+            var ffmpegCommandLineArguments = encodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
+            return await FileStreamResponseHelpers.GetTranscodedFile(
+                state,
+                isHeadRequest,
+                _streamHelper,
+                this,
+                _transcodingJobHelper,
+                ffmpegCommandLineArguments,
+                Request,
+                _transcodingJobType,
+                cancellationTokenSource).ConfigureAwait(false);
         }
     }
 }

+ 6 - 11
Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs

@@ -36,17 +36,14 @@ namespace Jellyfin.Api.Helpers
                 httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, useragent);
             }
 
-            var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false);
+            using var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false);
             var contentType = response.Content.Headers.ContentType.ToString();
 
             controller.Response.Headers[HeaderNames.AcceptRanges] = "none";
 
             if (isHeadRequest)
             {
-                using (response)
-                {
-                    return controller.File(Array.Empty<byte>(), contentType);
-                }
+                return controller.File(Array.Empty<byte>(), contentType);
             }
 
             return controller.File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), contentType);
@@ -74,7 +71,7 @@ namespace Jellyfin.Api.Helpers
                 return controller.NoContent();
             }
 
-            var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
+            using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
             return controller.File(stream, contentType);
         }
 
@@ -129,11 +126,9 @@ namespace Jellyfin.Api.Helpers
                     state.Dispose();
                 }
 
-                using (var memoryStream = new MemoryStream())
-                {
-                    await new ProgressiveFileCopier(streamHelper, outputPath).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false);
-                    return controller.File(memoryStream, contentType);
-                }
+                await using var memoryStream = new MemoryStream();
+                await new ProgressiveFileCopier(streamHelper, outputPath).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false);
+                return controller.File(memoryStream, contentType);
             }
             finally
             {

+ 19 - 49
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -74,7 +74,7 @@ namespace Jellyfin.Api.Helpers
             {
                 var timeSeek = httpRequest.Headers["TimeSeekRange.dlna.org"];
 
-                streamingRequest.StartTimeTicks = ParseTimeSeekHeader(timeSeek);
+                streamingRequest.StartTimeTicks = ParseTimeSeekHeader(timeSeek.ToString());
             }
 
             if (!string.IsNullOrWhiteSpace(streamingRequest.Params))
@@ -108,31 +108,22 @@ namespace Jellyfin.Api.Helpers
                 state.User = userManager.GetUserById(auth.UserId);
             }
 
-            /*
-            if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
-                (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
-                (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
-            {
-                state.SegmentLength = 6;
-            }
-            */
-
             if (state.IsVideoRequest && !string.IsNullOrWhiteSpace(state.Request.VideoCodec))
             {
-                state.SupportedVideoCodecs = state.Request.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+                state.SupportedVideoCodecs = state.Request.VideoCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
                 state.Request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
             }
 
             if (!string.IsNullOrWhiteSpace(streamingRequest.AudioCodec))
             {
-                state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+                state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
                 state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i))
                                            ?? state.SupportedAudioCodecs.FirstOrDefault();
             }
 
             if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec))
             {
-                state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+                state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
                 state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i))
                                               ?? state.SupportedSubtitleCodecs.FirstOrDefault();
             }
@@ -141,15 +132,6 @@ namespace Jellyfin.Api.Helpers
 
             state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
 
-            /*
-            var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
-                         item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
-            if (primaryImage != null)
-            {
-                state.AlbumCoverPath = primaryImage.Path;
-            }
-            */
-
             MediaSourceInfo? mediaSource = null;
             if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId))
             {
@@ -322,25 +304,24 @@ namespace Jellyfin.Api.Helpers
         /// </summary>
         /// <param name="value">The time seek header string.</param>
         /// <returns>A nullable <see cref="long"/> representing the seek time in ticks.</returns>
-        private static long? ParseTimeSeekHeader(string value)
+        private static long? ParseTimeSeekHeader(ReadOnlySpan<char> value)
         {
-            if (string.IsNullOrWhiteSpace(value))
+            if (value.IsEmpty)
             {
                 return null;
             }
 
-            const string Npt = "npt=";
-            if (!value.StartsWith(Npt, StringComparison.OrdinalIgnoreCase))
+            const string npt = "npt=";
+            if (!value.StartsWith(npt, StringComparison.OrdinalIgnoreCase))
             {
                 throw new ArgumentException("Invalid timeseek header");
             }
 
-            int index = value.IndexOf('-', StringComparison.InvariantCulture);
+            var index = value.IndexOf('-');
             value = index == -1
-                ? value.Substring(Npt.Length)
-                : value.Substring(Npt.Length, index - Npt.Length);
-
-            if (value.IndexOf(':', StringComparison.InvariantCulture) == -1)
+                ? value.Slice(npt.Length)
+                : value.Slice(npt.Length, index - npt.Length);
+            if (value.IndexOf(':') == -1)
             {
                 // Parses npt times in the format of '417.33'
                 if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds))
@@ -351,26 +332,15 @@ namespace Jellyfin.Api.Helpers
                 throw new ArgumentException("Invalid timeseek header");
             }
 
-            // Parses npt times in the format of '10:19:25.7'
-            var tokens = value.Split(new[] { ':' }, 3);
-            double secondsSum = 0;
-            var timeFactor = 3600;
-
-            foreach (var time in tokens)
+            try
             {
-                if (double.TryParse(time, NumberStyles.Any, CultureInfo.InvariantCulture, out var digit))
-                {
-                    secondsSum += digit * timeFactor;
-                }
-                else
-                {
-                    throw new ArgumentException("Invalid timeseek header");
-                }
-
-                timeFactor /= 60;
+                // Parses npt times in the format of '10:19:25.7'
+                return TimeSpan.Parse(value).Ticks;
+            }
+            catch
+            {
+                throw new ArgumentException("Invalid timeseek header");
             }
-
-            return TimeSpan.FromSeconds(secondsSum).Ticks;
         }
 
         /// <summary>

+ 5 - 5
Jellyfin.Api/Models/StreamingDtos/StreamState.cs

@@ -88,11 +88,11 @@ namespace Jellyfin.Api.Models.StreamingDtos
                 {
                     var userAgent = UserAgent ?? string.Empty;
 
-                    if (userAgent.IndexOf("AppleTV", StringComparison.OrdinalIgnoreCase) != -1 ||
-                        userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) != -1 ||
-                        userAgent.IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
-                        userAgent.IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
-                        userAgent.IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
+                    if (userAgent.IndexOf("AppleTV", StringComparison.OrdinalIgnoreCase) != -1
+                        || userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) != -1
+                        || userAgent.IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1
+                        || userAgent.IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1
+                        || userAgent.IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
                     {
                         return 6;
                     }