diff options
Diffstat (limited to 'Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs')
| -rw-r--r-- | Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 171 |
1 files changed, 85 insertions, 86 deletions
diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index 5bdd3fe2e..0f0a70c69 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -11,110 +11,109 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; -namespace Jellyfin.Api.Helpers +namespace Jellyfin.Api.Helpers; + +/// <summary> +/// The stream response helpers. +/// </summary> +public static class FileStreamResponseHelpers { /// <summary> - /// The stream response helpers. + /// Returns a static file from a remote source. /// </summary> - public static class FileStreamResponseHelpers + /// <param name="state">The current <see cref="StreamState"/>.</param> + /// <param name="httpClient">The <see cref="HttpClient"/> making the remote request.</param> + /// <param name="httpContext">The current http context.</param> + /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param> + /// <returns>A <see cref="Task{ActionResult}"/> containing the API response.</returns> + public static async Task<ActionResult> GetStaticRemoteStreamResult( + StreamState state, + HttpClient httpClient, + HttpContext httpContext, + CancellationToken cancellationToken = default) { - /// <summary> - /// Returns a static file from a remote source. - /// </summary> - /// <param name="state">The current <see cref="StreamState"/>.</param> - /// <param name="httpClient">The <see cref="HttpClient"/> making the remote request.</param> - /// <param name="httpContext">The current http context.</param> - /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param> - /// <returns>A <see cref="Task{ActionResult}"/> containing the API response.</returns> - public static async Task<ActionResult> GetStaticRemoteStreamResult( - StreamState state, - HttpClient httpClient, - HttpContext httpContext, - CancellationToken cancellationToken = default) + if (state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent)) { - if (state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent)) - { - httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, useragent); - } + httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, useragent); + } - // Can't dispose the response as it's required up the call chain. - var response = await httpClient.GetAsync(new Uri(state.MediaPath), cancellationToken).ConfigureAwait(false); - var contentType = response.Content.Headers.ContentType?.ToString() ?? MediaTypeNames.Text.Plain; + // Can't dispose the response as it's required up the call chain. + var response = await httpClient.GetAsync(new Uri(state.MediaPath), cancellationToken).ConfigureAwait(false); + var contentType = response.Content.Headers.ContentType?.ToString() ?? MediaTypeNames.Text.Plain; - httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; + httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; - return new FileStreamResult(await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false), contentType); - } + return new FileStreamResult(await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false), contentType); + } - /// <summary> - /// Returns a static file from the server. - /// </summary> - /// <param name="path">The path to the file.</param> - /// <param name="contentType">The content type of the file.</param> - /// <returns>An <see cref="ActionResult"/> the file.</returns> - public static ActionResult GetStaticFileResult( - string path, - string contentType) - { - return new PhysicalFileResult(path, contentType) { EnableRangeProcessing = true }; - } + /// <summary> + /// Returns a static file from the server. + /// </summary> + /// <param name="path">The path to the file.</param> + /// <param name="contentType">The content type of the file.</param> + /// <returns>An <see cref="ActionResult"/> the file.</returns> + public static ActionResult GetStaticFileResult( + string path, + string contentType) + { + return new PhysicalFileResult(path, contentType) { EnableRangeProcessing = true }; + } - /// <summary> - /// Returns a transcoded file from the server. - /// </summary> - /// <param name="state">The current <see cref="StreamState"/>.</param> - /// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param> - /// <param name="httpContext">The current http context.</param> - /// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param> - /// <param name="ffmpegCommandLineArguments">The command line arguments to start ffmpeg.</param> - /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param> - /// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param> - /// <returns>A <see cref="Task{ActionResult}"/> containing the transcoded file.</returns> - public static async Task<ActionResult> GetTranscodedFile( - StreamState state, - bool isHeadRequest, - HttpContext httpContext, - TranscodingJobHelper transcodingJobHelper, - string ffmpegCommandLineArguments, - TranscodingJobType transcodingJobType, - CancellationTokenSource cancellationTokenSource) - { - // Use the command line args with a dummy playlist path - var outputPath = state.OutputFilePath; + /// <summary> + /// Returns a transcoded file from the server. + /// </summary> + /// <param name="state">The current <see cref="StreamState"/>.</param> + /// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param> + /// <param name="httpContext">The current http context.</param> + /// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param> + /// <param name="ffmpegCommandLineArguments">The command line arguments to start ffmpeg.</param> + /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param> + /// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param> + /// <returns>A <see cref="Task{ActionResult}"/> containing the transcoded file.</returns> + public static async Task<ActionResult> GetTranscodedFile( + StreamState state, + bool isHeadRequest, + HttpContext httpContext, + TranscodingJobHelper transcodingJobHelper, + string ffmpegCommandLineArguments, + TranscodingJobType transcodingJobType, + CancellationTokenSource cancellationTokenSource) + { + // Use the command line args with a dummy playlist path + var outputPath = state.OutputFilePath; - httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; + httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none"; - var contentType = state.GetMimeType(outputPath); + var contentType = state.GetMimeType(outputPath); - // Headers only - if (isHeadRequest) - { - httpContext.Response.Headers[HeaderNames.ContentType] = contentType; - return new OkResult(); - } + // Headers only + if (isHeadRequest) + { + httpContext.Response.Headers[HeaderNames.ContentType] = contentType; + return new OkResult(); + } - var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); - await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); - try + var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); + await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); + try + { + TranscodingJobDto? job; + if (!File.Exists(outputPath)) { - TranscodingJobDto? job; - if (!File.Exists(outputPath)) - { - job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); - } - else - { - job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); - state.Dispose(); - } - - var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper); - return new FileStreamResult(stream, contentType); + job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); } - finally + else { - transcodingLock.Release(); + job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); + state.Dispose(); } + + var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper); + return new FileStreamResult(stream, contentType); + } + finally + { + transcodingLock.Release(); } } } |
