diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index 4d29d38807..81492ed4aa 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/Jellyfin.Api/Controllers/AudioController.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.Models.StreamingDtos;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -141,10 +142,10 @@ namespace Jellyfin.Api.Controllers
/// Optional. The .
/// Optional. The streaming options.
/// A containing the audio file.
- [HttpGet("{itemId}/stream.{container}")]
- [HttpGet("{itemId}/stream")]
- [HttpHead("{itemId}/stream.{container}")]
+ [HttpGet("{itemId}/{stream=stream}.{container?}")]
[HttpGet("{itemId}/stream")]
+ [HttpHead("{itemId}/{stream=stream}.{container?}")]
+ [HttpHead("{itemId}/stream")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task GetAudioStream(
[FromRoute] Guid itemId,
@@ -201,21 +202,61 @@ namespace Jellyfin.Api.Controllers
var cancellationTokenSource = new CancellationTokenSource();
+ StreamingRequestDto streamingRequest = new StreamingRequestDto
+ {
+ Id = itemId,
+ Container = container,
+ Static = @static.HasValue ? @static.Value : true,
+ Params = @params,
+ Tag = tag,
+ DeviceProfileId = deviceProfileId,
+ PlaySessionId = playSessionId,
+ SegmentContainer = segmentContainer,
+ SegmentLength = segmentLength,
+ MinSegments = minSegments,
+ MediaSourceId = mediaSourceId,
+ DeviceId = deviceId,
+ AudioCodec = audioCodec,
+ EnableAutoStreamCopy = enableAutoStreamCopy.HasValue ? enableAutoStreamCopy.Value : true,
+ AllowAudioStreamCopy = allowAudioStreamCopy.HasValue ? allowAudioStreamCopy.Value : true,
+ AllowVideoStreamCopy = allowVideoStreamCopy.HasValue ? allowVideoStreamCopy.Value : true,
+ BreakOnNonKeyFrames = breakOnNonKeyFrames.HasValue ? breakOnNonKeyFrames.Value : false,
+ AudioSampleRate = audioSampleRate,
+ MaxAudioChannels = maxAudioChannels,
+ AudioBitRate = audioBitRate,
+ MaxAudioBitDepth = maxAudioBitDepth,
+ AudioChannels = audioChannels,
+ Profile = profile,
+ Level = level,
+ Framerate = framerate,
+ MaxFramerate = maxFramerate,
+ CopyTimestamps = copyTimestamps.HasValue ? copyTimestamps.Value : true,
+ StartTimeTicks = startTimeTicks,
+ Width = width,
+ Height = height,
+ VideoBitRate = videoBitRate,
+ SubtitleStreamIndex = subtitleStreamIndex,
+ SubtitleMethod = subtitleMethod,
+ MaxRefFrames = maxRefFrames,
+ MaxVideoBitDepth = maxVideoBitDepth,
+ RequireAvc = requireAvc.HasValue ? requireAvc.Value : true,
+ DeInterlace = deInterlace.HasValue ? deInterlace.Value : true,
+ RequireNonAnamorphic = requireNonAnamorphic.HasValue ? requireNonAnamorphic.Value : true,
+ TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
+ CpuCoreLimit = cpuCoreLimit,
+ LiveStreamId = liveStreamId,
+ EnableMpegtsM2TsMode = enableMpegtsM2TsMode.HasValue ? enableMpegtsM2TsMode.Value : true,
+ VideoCodec = videoCodec,
+ SubtitleCodec = subtitleCodec,
+ TranscodeReasons = transcodingReasons,
+ AudioStreamIndex = audioStreamIndex,
+ VideoStreamIndex = videoStreamIndex,
+ Context = context,
+ StreamOptions = streamOptions
+ };
+
var state = await StreamingHelpers.GetStreamingState(
- itemId,
- startTimeTicks,
- audioCodec,
- subtitleCodec,
- videoCodec,
- @params,
- @static,
- container,
- liveStreamId,
- playSessionId,
- mediaSourceId,
- deviceId,
- deviceProfileId,
- audioBitRate,
+ streamingRequest,
Request,
_authContext,
_mediaSourceManager,
@@ -230,7 +271,6 @@ namespace Jellyfin.Api.Controllers
_deviceManager,
_transcodingJobHelper,
_transcodingJobType,
- false,
cancellationTokenSource.Token)
.ConfigureAwait(false);
@@ -255,7 +295,7 @@ namespace Jellyfin.Api.Controllers
using (state)
{
- return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, cancellationTokenSource).ConfigureAwait(false);
+ return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this).ConfigureAwait(false);
}
}
@@ -297,8 +337,6 @@ namespace Jellyfin.Api.Controllers
return FileStreamResponseHelpers.GetStaticFileResult(
state.MediaPath,
contentType,
- _fileSystem.GetLastWriteTimeUtc(state.MediaPath),
- cacheDuration,
isHeadRequest,
this);
}
diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
index 6ba74d5901..9f16b53236 100644
--- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
+++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
@@ -23,13 +23,11 @@ namespace Jellyfin.Api.Helpers
/// The current .
/// Whether the current request is a HTTP HEAD request so only the headers get returned.
/// The managing the response.
- /// The .
/// A containing the API response.
public static async Task GetStaticRemoteStreamResult(
StreamState state,
bool isHeadRequest,
- ControllerBase controller,
- CancellationTokenSource cancellationTokenSource)
+ ControllerBase controller)
{
HttpClient httpClient = new HttpClient();
@@ -59,16 +57,12 @@ namespace Jellyfin.Api.Helpers
///
/// The path to the file.
/// The content type of the file.
- /// The of the last modification of the file.
- /// The cache duration of the file.
/// Whether the current request is a HTTP HEAD request so only the headers get returned.
/// The managing the response.
/// An the file.
public static ActionResult GetStaticFileResult(
string path,
string contentType,
- DateTime dateLastModified,
- TimeSpan? cacheDuration,
bool isHeadRequest,
ControllerBase controller)
{
@@ -135,10 +129,11 @@ namespace Jellyfin.Api.Helpers
state.Dispose();
}
- Stream stream = new MemoryStream();
-
- await new ProgressiveFileCopier(streamHelper, outputPath).WriteToAsync(stream, CancellationToken.None).ConfigureAwait(false);
- return controller.File(stream, contentType);
+ 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 ee1f1efce2..71bf053f58 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -30,22 +30,29 @@ namespace Jellyfin.Api.Helpers
///
public static class StreamingHelpers
{
+ ///
+ /// Gets the current streaming state.
+ ///
+ /// The .
+ /// The .
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Initialized .
+ /// The .
+ /// The .
+ /// A containing the current .
public static async Task GetStreamingState(
- Guid itemId,
- long? startTimeTicks,
- string? audioCodec,
- string? subtitleCodec,
- string? videoCodec,
- string? @params,
- bool? @static,
- string? container,
- string? liveStreamId,
- string? playSessionId,
- string? mediaSourceId,
- string? deviceId,
- string? deviceProfileId,
- int? audioBitRate,
- HttpRequest request,
+ StreamingRequestDto streamingRequest,
+ HttpRequest httpRequest,
IAuthorizationContext authorizationContext,
IMediaSourceManager mediaSourceManager,
IUserManager userManager,
@@ -59,49 +66,43 @@ namespace Jellyfin.Api.Helpers
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
TranscodingJobType transcodingJobType,
- bool isVideoRequest,
CancellationToken cancellationToken)
{
EncodingHelper encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
// Parse the DLNA time seek header
- if (!startTimeTicks.HasValue)
+ if (!streamingRequest.StartTimeTicks.HasValue)
{
- var timeSeek = request.Headers["TimeSeekRange.dlna.org"];
+ var timeSeek = httpRequest.Headers["TimeSeekRange.dlna.org"];
- startTimeTicks = ParseTimeSeekHeader(timeSeek);
+ streamingRequest.StartTimeTicks = ParseTimeSeekHeader(timeSeek);
}
- if (!string.IsNullOrWhiteSpace(@params))
+ if (!string.IsNullOrWhiteSpace(streamingRequest.Params))
{
- // What is this?
- ParseParams(request);
+ ParseParams(streamingRequest);
}
- var streamOptions = ParseStreamOptions(request.Query);
+ streamingRequest.StreamOptions = ParseStreamOptions(httpRequest.Query);
- var url = request.Path.Value.Split('.').Last();
+ var url = httpRequest.Path.Value.Split('.').Last();
- if (string.IsNullOrEmpty(audioCodec))
+ if (string.IsNullOrEmpty(streamingRequest.AudioCodec))
{
- audioCodec = encodingHelper.InferAudioCodec(url);
+ streamingRequest.AudioCodec = encodingHelper.InferAudioCodec(url);
}
- var enableDlnaHeaders = !string.IsNullOrWhiteSpace(@params) ||
- string.Equals(request.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
+ var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) ||
+ string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
{
- // TODO request was the StreamingRequest living in MediaBrowser.Api.Playback.Progressive
- // Request = request,
- DeviceId = deviceId,
- PlaySessionId = playSessionId,
- LiveStreamId = liveStreamId,
+ Request = streamingRequest,
RequestedUrl = url,
- UserAgent = request.Headers[HeaderNames.UserAgent],
+ UserAgent = httpRequest.Headers[HeaderNames.UserAgent],
EnableDlnaHeaders = enableDlnaHeaders
};
- var auth = authorizationContext.GetAuthorizationInfo(request);
+ var auth = authorizationContext.GetAuthorizationInfo(httpRequest);
if (!auth.UserId.Equals(Guid.Empty))
{
state.User = userManager.GetUserById(auth.UserId);
@@ -116,27 +117,27 @@ namespace Jellyfin.Api.Helpers
}
*/
- if (state.IsVideoRequest && !string.IsNullOrWhiteSpace(state.VideoCodec))
+ if (state.IsVideoRequest && !string.IsNullOrWhiteSpace(state.Request.VideoCodec))
{
- state.SupportedVideoCodecs = state.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- state.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
+ state.SupportedVideoCodecs = state.Request.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+ state.Request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
}
- if (!string.IsNullOrWhiteSpace(audioCodec))
+ if (!string.IsNullOrWhiteSpace(streamingRequest.AudioCodec))
{
- state.SupportedAudioCodecs = audioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- state.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i))
+ state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+ state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i))
?? state.SupportedAudioCodecs.FirstOrDefault();
}
- if (!string.IsNullOrWhiteSpace(subtitleCodec))
+ if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec))
{
- state.SupportedSubtitleCodecs = subtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- state.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i))
+ state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
+ state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i))
?? state.SupportedSubtitleCodecs.FirstOrDefault();
}
- var item = libraryManager.GetItemById(itemId);
+ var item = libraryManager.GetItemById(streamingRequest.Id);
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
@@ -150,10 +151,10 @@ namespace Jellyfin.Api.Helpers
*/
MediaSourceInfo? mediaSource = null;
- if (string.IsNullOrWhiteSpace(liveStreamId))
+ if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId))
{
- var currentJob = !string.IsNullOrWhiteSpace(playSessionId)
- ? transcodingJobHelper.GetTranscodingJob(playSessionId)
+ var currentJob = !string.IsNullOrWhiteSpace(streamingRequest.PlaySessionId)
+ ? transcodingJobHelper.GetTranscodingJob(streamingRequest.PlaySessionId)
: null;
if (currentJob != null)
@@ -163,13 +164,13 @@ namespace Jellyfin.Api.Helpers
if (mediaSource == null)
{
- var mediaSources = await mediaSourceManager.GetPlaybackMediaSources(libraryManager.GetItemById(itemId), null, false, false, cancellationToken).ConfigureAwait(false);
+ var mediaSources = await mediaSourceManager.GetPlaybackMediaSources(libraryManager.GetItemById(streamingRequest.Id), null, false, false, cancellationToken).ConfigureAwait(false);
- mediaSource = string.IsNullOrEmpty(mediaSourceId)
+ mediaSource = string.IsNullOrEmpty(streamingRequest.MediaSourceId)
? mediaSources[0]
- : mediaSources.Find(i => string.Equals(i.Id, mediaSourceId, StringComparison.InvariantCulture));
+ : mediaSources.Find(i => string.Equals(i.Id, streamingRequest.MediaSourceId, StringComparison.InvariantCulture));
- if (mediaSource == null && Guid.Parse(mediaSourceId) == itemId)
+ if (mediaSource == null && Guid.Parse(streamingRequest.MediaSourceId) == streamingRequest.Id)
{
mediaSource = mediaSources[0];
}
@@ -177,7 +178,7 @@ namespace Jellyfin.Api.Helpers
}
else
{
- var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(liveStreamId, cancellationToken).ConfigureAwait(false);
+ var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(streamingRequest.LiveStreamId, cancellationToken).ConfigureAwait(false);
mediaSource = liveStreamInfo.Item1;
state.DirectStreamProvider = liveStreamInfo.Item2;
}
@@ -186,28 +187,28 @@ namespace Jellyfin.Api.Helpers
var containerInternal = Path.GetExtension(state.RequestedUrl);
- if (string.IsNullOrEmpty(container))
+ if (string.IsNullOrEmpty(streamingRequest.Container))
{
- containerInternal = container;
+ containerInternal = streamingRequest.Container;
}
if (string.IsNullOrEmpty(containerInternal))
{
- containerInternal = (@static.HasValue && @static.Value) ? StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio) : GetOutputFileExtension(state);
+ containerInternal = (streamingRequest.Static && streamingRequest.Static) ? StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio) : GetOutputFileExtension(state);
}
state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
- state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(audioBitRate, state.AudioStream);
+ state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, state.AudioStream);
- state.OutputAudioCodec = audioCodec;
+ state.OutputAudioCodec = streamingRequest.AudioCodec;
state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
- if (isVideoRequest)
+ if (state.VideoRequest != null)
{
- state.OutputVideoCodec = state.VideoCodec;
- state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
+ state.OutputVideoCodec = state.Request.VideoCodec;
+ state.OutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
encodingHelper.TryStreamCopy(state);
@@ -220,21 +221,21 @@ namespace Jellyfin.Api.Helpers
state.OutputVideoBitrate.Value,
state.VideoStream?.Codec,
state.OutputVideoCodec,
- videoRequest.MaxWidth,
- videoRequest.MaxHeight);
+ state.VideoRequest.MaxWidth,
+ state.VideoRequest.MaxHeight);
- videoRequest.MaxWidth = resolution.MaxWidth;
- videoRequest.MaxHeight = resolution.MaxHeight;
+ state.VideoRequest.MaxWidth = resolution.MaxWidth;
+ state.VideoRequest.MaxHeight = resolution.MaxHeight;
}
}
- ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, request, deviceProfileId, @static);
+ ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static);
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
? GetOutputFileExtension(state)
: ('.' + state.OutputContainer);
- state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, deviceId, playSessionId);
+ state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId);
return state;
}
@@ -319,7 +320,7 @@ namespace Jellyfin.Api.Helpers
///
/// The time seek header string.
/// A nullable representing the seek time in ticks.
- public static long? ParseTimeSeekHeader(string value)
+ private static long? ParseTimeSeekHeader(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
@@ -375,7 +376,7 @@ namespace Jellyfin.Api.Helpers
///
/// The query string.
/// A containing the stream options.
- public static Dictionary ParseStreamOptions(IQueryCollection queryString)
+ private static Dictionary ParseStreamOptions(IQueryCollection queryString)
{
Dictionary streamOptions = new Dictionary();
foreach (var param in queryString)
@@ -398,7 +399,7 @@ namespace Jellyfin.Api.Helpers
/// The current .
/// The of the response.
/// The start time in ticks.
- public static void AddTimeSeekResponseHeaders(StreamState state, IHeaderDictionary responseHeaders, long? startTimeTicks)
+ private static void AddTimeSeekResponseHeaders(StreamState state, IHeaderDictionary responseHeaders, long? startTimeTicks)
{
var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks!.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture);
var startSeconds = TimeSpan.FromTicks(startTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
@@ -420,7 +421,7 @@ namespace Jellyfin.Api.Helpers
///
/// The state.
/// System.String.
- public static string? GetOutputFileExtension(StreamState state)
+ private static string? GetOutputFileExtension(StreamState state)
{
var ext = Path.GetExtension(state.RequestedUrl);
@@ -432,7 +433,7 @@ namespace Jellyfin.Api.Helpers
// Try to infer based on the desired video codec
if (state.IsVideoRequest)
{
- var videoCodec = state.VideoCodec;
+ var videoCodec = state.Request.VideoCodec;
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) ||
string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase))
@@ -459,7 +460,7 @@ namespace Jellyfin.Api.Helpers
// Try to infer based on the desired audio codec
if (!state.IsVideoRequest)
{
- var audioCodec = state.AudioCodec;
+ var audioCodec = state.Request.AudioCodec;
if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
{
@@ -570,7 +571,7 @@ namespace Jellyfin.Api.Helpers
// state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
- if (!state.IsVideoRequest)
+ if (state.VideoRequest != null)
{
state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
@@ -583,11 +584,16 @@ namespace Jellyfin.Api.Helpers
/// Parses the parameters.
///
/// The request.
- private void ParseParams(StreamRequest request)
+ private static void ParseParams(StreamingRequestDto request)
{
+ if (string.IsNullOrEmpty(request.Params))
+ {
+ return;
+ }
+
var vals = request.Params.Split(';');
- var videoRequest = request as VideoStreamRequest;
+ var videoRequest = request as VideoRequestDto;
for (var i = 0; i < vals.Length; i++)
{
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 4605c01831..c84135085f 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -443,7 +443,7 @@ namespace Jellyfin.Api.Helpers
job.BitRate = bitRate;
}
- var deviceId = state.DeviceId;
+ var deviceId = state.Request.DeviceId;
if (!string.IsNullOrWhiteSpace(deviceId))
{
@@ -486,7 +486,7 @@ namespace Jellyfin.Api.Helpers
HttpRequest request,
TranscodingJobType transcodingJobType,
CancellationTokenSource cancellationTokenSource,
- string workingDirectory = null)
+ string? workingDirectory = null)
{
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
@@ -525,12 +525,12 @@ namespace Jellyfin.Api.Helpers
var transcodingJob = this.OnTranscodeBeginning(
outputPath,
- state.PlaySessionId,
+ state.Request.PlaySessionId,
state.MediaSource.LiveStreamId,
Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
transcodingJobType,
process,
- state.DeviceId,
+ state.Request.DeviceId,
state,
cancellationTokenSource);
@@ -706,9 +706,9 @@ namespace Jellyfin.Api.Helpers
_transcodingLocks.Remove(path);
}
- if (!string.IsNullOrWhiteSpace(state.DeviceId))
+ if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
{
- _sessionManager.ClearTranscodingInfo(state.DeviceId);
+ _sessionManager.ClearTranscodingInfo(state.Request.DeviceId);
}
}
@@ -747,7 +747,7 @@ namespace Jellyfin.Api.Helpers
state.IsoMount = await _isoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
- if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.LiveStreamId))
+ if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
{
var liveStreamResponse = await _mediaSourceManager.OpenLiveStream(
new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken },
diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs
index db7cc6a75c..70a13d745f 100644
--- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs
+++ b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs
@@ -19,7 +19,7 @@ namespace Jellyfin.Api.Models.StreamingDtos
///
/// Initializes a new instance of the class.
///
- /// Instance of the interface.
+ /// Instance of the interface.
/// The .
/// The singleton.
public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, TranscodingJobHelper transcodingJobHelper)
@@ -34,29 +34,28 @@ namespace Jellyfin.Api.Models.StreamingDtos
///
public string? RequestedUrl { get; set; }
- // ///
- // /// Gets or sets the request.
- // ///
- // public StreamRequest Request
- // {
- // get => (StreamRequest)BaseRequest;
- // set
- // {
- // BaseRequest = value;
- //
- // IsVideoRequest = VideoRequest != null;
- // }
- // }
+ ///
+ /// Gets or sets the request.
+ ///
+ public StreamingRequestDto Request
+ {
+ get => (StreamingRequestDto)BaseRequest;
+ set
+ {
+ BaseRequest = value;
+ IsVideoRequest = VideoRequest != null;
+ }
+ }
///
/// Gets or sets the transcoding throttler.
///
public TranscodingThrottler? TranscodingThrottler { get; set; }
- /*///
+ ///
/// Gets the video request.
///
- public VideoStreamRequest VideoRequest => Request as VideoStreamRequest;*/
+ public VideoRequestDto? VideoRequest => Request! as VideoRequestDto;
///
/// Gets or sets the direct stream provicer.
@@ -68,10 +67,10 @@ namespace Jellyfin.Api.Models.StreamingDtos
///
public string? WaitForPath { get; set; }
- /*///
+ ///
/// Gets a value indicating whether the request outputs video.
///
- public bool IsOutputVideo => Request is VideoStreamRequest;*/
+ public bool IsOutputVideo => Request is VideoRequestDto;
///
/// Gets the segment length.
@@ -161,36 +160,6 @@ namespace Jellyfin.Api.Models.StreamingDtos
///
public TranscodingJobDto? TranscodingJob { get; set; }
- ///
- /// Gets or sets the device id.
- ///
- public string? DeviceId { get; set; }
-
- ///
- /// Gets or sets the play session id.
- ///
- public string? PlaySessionId { get; set; }
-
- ///
- /// Gets or sets the live stream id.
- ///
- public string? LiveStreamId { get; set; }
-
- ///
- /// Gets or sets the video coded.
- ///
- public string? VideoCodec { get; set; }
-
- ///
- /// Gets or sets the audio codec.
- ///
- public string? AudioCodec { get; set; }
-
- ///
- /// Gets or sets the subtitle codec.
- ///
- public string? SubtitleCodec { get; set; }
-
///
public void Dispose()
{
@@ -219,7 +188,7 @@ namespace Jellyfin.Api.Models.StreamingDtos
{
// REVIEW: Is this the right place for this?
if (MediaSource.RequiresClosing
- && string.IsNullOrWhiteSpace(LiveStreamId)
+ && string.IsNullOrWhiteSpace(Request.LiveStreamId)
&& !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
{
_mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).GetAwaiter().GetResult();
diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs b/Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs
new file mode 100644
index 0000000000..1791b03706
--- /dev/null
+++ b/Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs
@@ -0,0 +1,45 @@
+using MediaBrowser.Controller.MediaEncoding;
+
+namespace Jellyfin.Api.Models.StreamingDtos
+{
+ ///
+ /// The audio streaming request dto.
+ ///
+ public class StreamingRequestDto : BaseEncodingJobOptions
+ {
+ ///
+ /// Gets or sets the device profile.
+ ///
+ public string? DeviceProfileId { get; set; }
+
+ ///
+ /// Gets or sets the params.
+ ///
+ public string? Params { get; set; }
+
+ ///
+ /// Gets or sets the play session id.
+ ///
+ public string? PlaySessionId { get; set; }
+
+ ///
+ /// Gets or sets the tag.
+ ///
+ public string? Tag { get; set; }
+
+ ///
+ /// Gets or sets the segment container.
+ ///
+ public string? SegmentContainer { get; set; }
+
+ ///
+ /// Gets or sets the segment length.
+ ///
+ public int? SegmentLength { get; set; }
+
+ ///
+ /// Gets or sets the min segments.
+ ///
+ public int? MinSegments { get; set; }
+ }
+}
diff --git a/Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs b/Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs
new file mode 100644
index 0000000000..cce2a89d49
--- /dev/null
+++ b/Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs
@@ -0,0 +1,19 @@
+namespace Jellyfin.Api.Models.StreamingDtos
+{
+ ///
+ /// The video request dto.
+ ///
+ public class VideoRequestDto : StreamingRequestDto
+ {
+ ///
+ /// Gets a value indicating whether this instance has fixed resolution.
+ ///
+ /// true if this instance has fixed resolution; otherwise, false.
+ public bool HasFixedResolution => Width.HasValue || Height.HasValue;
+
+ ///
+ /// Gets or sets a value indicating whether to enable subtitles in the manifest.
+ ///
+ public bool EnableSubtitlesInManifest { get; set; }
+ }
+}