aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Api/Controllers/AudioController.cs107
-rw-r--r--Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs17
-rw-r--r--Jellyfin.Api/Helpers/StreamingHelpers.cs68
-rw-r--r--Jellyfin.Api/Models/StreamingDtos/StreamState.cs10
4 files changed, 58 insertions, 144 deletions
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index d8c67cc24..7405c26fb 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/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);
}
}
}
diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
index ddca2f1ae..636f47f5f 100644
--- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
+++ b/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
{
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 0b18756d6..b12590080 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/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>
diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs
index df5e21dac..e95f2d1f4 100644
--- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs
+++ b/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;
}