mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-17 06:23:03 +03:00
Move AudioService to Jellyfin.Api
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
|
using Jellyfin.Api.Models.StreamingDtos;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
@@ -141,10 +142,10 @@ namespace Jellyfin.Api.Controllers
|
|||||||
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
|
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
|
||||||
/// <param name="streamOptions">Optional. The streaming options.</param>
|
/// <param name="streamOptions">Optional. The streaming options.</param>
|
||||||
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
||||||
[HttpGet("{itemId}/stream.{container}")]
|
[HttpGet("{itemId}/{stream=stream}.{container?}")]
|
||||||
[HttpGet("{itemId}/stream")]
|
|
||||||
[HttpHead("{itemId}/stream.{container}")]
|
|
||||||
[HttpGet("{itemId}/stream")]
|
[HttpGet("{itemId}/stream")]
|
||||||
|
[HttpHead("{itemId}/{stream=stream}.{container?}")]
|
||||||
|
[HttpHead("{itemId}/stream")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult> GetAudioStream(
|
public async Task<ActionResult> GetAudioStream(
|
||||||
[FromRoute] Guid itemId,
|
[FromRoute] Guid itemId,
|
||||||
@@ -201,21 +202,61 @@ namespace Jellyfin.Api.Controllers
|
|||||||
|
|
||||||
var cancellationTokenSource = new CancellationTokenSource();
|
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(
|
var state = await StreamingHelpers.GetStreamingState(
|
||||||
itemId,
|
streamingRequest,
|
||||||
startTimeTicks,
|
|
||||||
audioCodec,
|
|
||||||
subtitleCodec,
|
|
||||||
videoCodec,
|
|
||||||
@params,
|
|
||||||
@static,
|
|
||||||
container,
|
|
||||||
liveStreamId,
|
|
||||||
playSessionId,
|
|
||||||
mediaSourceId,
|
|
||||||
deviceId,
|
|
||||||
deviceProfileId,
|
|
||||||
audioBitRate,
|
|
||||||
Request,
|
Request,
|
||||||
_authContext,
|
_authContext,
|
||||||
_mediaSourceManager,
|
_mediaSourceManager,
|
||||||
@@ -230,7 +271,6 @@ namespace Jellyfin.Api.Controllers
|
|||||||
_deviceManager,
|
_deviceManager,
|
||||||
_transcodingJobHelper,
|
_transcodingJobHelper,
|
||||||
_transcodingJobType,
|
_transcodingJobType,
|
||||||
false,
|
|
||||||
cancellationTokenSource.Token)
|
cancellationTokenSource.Token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -255,7 +295,7 @@ namespace Jellyfin.Api.Controllers
|
|||||||
|
|
||||||
using (state)
|
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(
|
return FileStreamResponseHelpers.GetStaticFileResult(
|
||||||
state.MediaPath,
|
state.MediaPath,
|
||||||
contentType,
|
contentType,
|
||||||
_fileSystem.GetLastWriteTimeUtc(state.MediaPath),
|
|
||||||
cacheDuration,
|
|
||||||
isHeadRequest,
|
isHeadRequest,
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,11 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// <param name="state">The current <see cref="StreamState"/>.</param>
|
/// <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="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param>
|
||||||
/// <param name="controller">The <see cref="ControllerBase"/> managing the response.</param>
|
/// <param name="controller">The <see cref="ControllerBase"/> managing the response.</param>
|
||||||
/// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param>
|
|
||||||
/// <returns>A <see cref="Task{ActionResult}"/> containing the API response.</returns>
|
/// <returns>A <see cref="Task{ActionResult}"/> containing the API response.</returns>
|
||||||
public static async Task<ActionResult> GetStaticRemoteStreamResult(
|
public static async Task<ActionResult> GetStaticRemoteStreamResult(
|
||||||
StreamState state,
|
StreamState state,
|
||||||
bool isHeadRequest,
|
bool isHeadRequest,
|
||||||
ControllerBase controller,
|
ControllerBase controller)
|
||||||
CancellationTokenSource cancellationTokenSource)
|
|
||||||
{
|
{
|
||||||
HttpClient httpClient = new HttpClient();
|
HttpClient httpClient = new HttpClient();
|
||||||
|
|
||||||
@@ -59,16 +57,12 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to the file.</param>
|
/// <param name="path">The path to the file.</param>
|
||||||
/// <param name="contentType">The content type of the file.</param>
|
/// <param name="contentType">The content type of the file.</param>
|
||||||
/// <param name="dateLastModified">The <see cref="DateTime"/> of the last modification of the file.</param>
|
|
||||||
/// <param name="cacheDuration">The cache duration of the file.</param>
|
|
||||||
/// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param>
|
/// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param>
|
||||||
/// <param name="controller">The <see cref="ControllerBase"/> managing the response.</param>
|
/// <param name="controller">The <see cref="ControllerBase"/> managing the response.</param>
|
||||||
/// <returns>An <see cref="ActionResult"/> the file.</returns>
|
/// <returns>An <see cref="ActionResult"/> the file.</returns>
|
||||||
public static ActionResult GetStaticFileResult(
|
public static ActionResult GetStaticFileResult(
|
||||||
string path,
|
string path,
|
||||||
string contentType,
|
string contentType,
|
||||||
DateTime dateLastModified,
|
|
||||||
TimeSpan? cacheDuration,
|
|
||||||
bool isHeadRequest,
|
bool isHeadRequest,
|
||||||
ControllerBase controller)
|
ControllerBase controller)
|
||||||
{
|
{
|
||||||
@@ -135,10 +129,11 @@ namespace Jellyfin.Api.Helpers
|
|||||||
state.Dispose();
|
state.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream stream = new MemoryStream();
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
await new ProgressiveFileCopier(streamHelper, outputPath).WriteToAsync(stream, CancellationToken.None).ConfigureAwait(false);
|
await new ProgressiveFileCopier(streamHelper, outputPath).WriteToAsync(memoryStream, CancellationToken.None).ConfigureAwait(false);
|
||||||
return controller.File(stream, contentType);
|
return controller.File(memoryStream, contentType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,22 +30,29 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class StreamingHelpers
|
public static class StreamingHelpers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current streaming state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamingRequest">The <see cref="StreamingRequestDto"/>.</param>
|
||||||
|
/// <param name="httpRequest">The <see cref="HttpRequest"/>.</param>
|
||||||
|
/// <param name="authorizationContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
|
||||||
|
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
|
||||||
|
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
||||||
|
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
||||||
|
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||||
|
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
|
||||||
|
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||||
|
/// <param name="subtitleEncoder">Instance of the <see cref="ISubtitleEncoder"/> interface.</param>
|
||||||
|
/// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
|
||||||
|
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
|
||||||
|
/// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
|
||||||
|
/// <param name="transcodingJobHelper">Initialized <see cref="TranscodingJobHelper"/>.</param>
|
||||||
|
/// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
|
||||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> containing the current <see cref="StreamState"/>.</returns>
|
||||||
public static async Task<StreamState> GetStreamingState(
|
public static async Task<StreamState> GetStreamingState(
|
||||||
Guid itemId,
|
StreamingRequestDto streamingRequest,
|
||||||
long? startTimeTicks,
|
HttpRequest httpRequest,
|
||||||
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,
|
|
||||||
IAuthorizationContext authorizationContext,
|
IAuthorizationContext authorizationContext,
|
||||||
IMediaSourceManager mediaSourceManager,
|
IMediaSourceManager mediaSourceManager,
|
||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
@@ -59,49 +66,43 @@ namespace Jellyfin.Api.Helpers
|
|||||||
IDeviceManager deviceManager,
|
IDeviceManager deviceManager,
|
||||||
TranscodingJobHelper transcodingJobHelper,
|
TranscodingJobHelper transcodingJobHelper,
|
||||||
TranscodingJobType transcodingJobType,
|
TranscodingJobType transcodingJobType,
|
||||||
bool isVideoRequest,
|
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
EncodingHelper encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
|
EncodingHelper encodingHelper = new EncodingHelper(mediaEncoder, fileSystem, subtitleEncoder, configuration);
|
||||||
// Parse the DLNA time seek header
|
// 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(streamingRequest);
|
||||||
ParseParams(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) ||
|
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) ||
|
||||||
string.Equals(request.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
|
string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
|
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
|
||||||
{
|
{
|
||||||
// TODO request was the StreamingRequest living in MediaBrowser.Api.Playback.Progressive
|
Request = streamingRequest,
|
||||||
// Request = request,
|
|
||||||
DeviceId = deviceId,
|
|
||||||
PlaySessionId = playSessionId,
|
|
||||||
LiveStreamId = liveStreamId,
|
|
||||||
RequestedUrl = url,
|
RequestedUrl = url,
|
||||||
UserAgent = request.Headers[HeaderNames.UserAgent],
|
UserAgent = httpRequest.Headers[HeaderNames.UserAgent],
|
||||||
EnableDlnaHeaders = enableDlnaHeaders
|
EnableDlnaHeaders = enableDlnaHeaders
|
||||||
};
|
};
|
||||||
|
|
||||||
var auth = authorizationContext.GetAuthorizationInfo(request);
|
var auth = authorizationContext.GetAuthorizationInfo(httpRequest);
|
||||||
if (!auth.UserId.Equals(Guid.Empty))
|
if (!auth.UserId.Equals(Guid.Empty))
|
||||||
{
|
{
|
||||||
state.User = userManager.GetUserById(auth.UserId);
|
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.SupportedVideoCodecs = state.Request.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
|
||||||
state.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
|
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.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
|
||||||
state.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i))
|
state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i))
|
||||||
?? state.SupportedAudioCodecs.FirstOrDefault();
|
?? state.SupportedAudioCodecs.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(subtitleCodec))
|
if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec))
|
||||||
{
|
{
|
||||||
state.SupportedSubtitleCodecs = subtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
|
state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
|
||||||
state.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i))
|
state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i))
|
||||||
?? state.SupportedSubtitleCodecs.FirstOrDefault();
|
?? state.SupportedSubtitleCodecs.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = libraryManager.GetItemById(itemId);
|
var item = libraryManager.GetItemById(streamingRequest.Id);
|
||||||
|
|
||||||
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
@@ -150,10 +151,10 @@ namespace Jellyfin.Api.Helpers
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
MediaSourceInfo? mediaSource = null;
|
MediaSourceInfo? mediaSource = null;
|
||||||
if (string.IsNullOrWhiteSpace(liveStreamId))
|
if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId))
|
||||||
{
|
{
|
||||||
var currentJob = !string.IsNullOrWhiteSpace(playSessionId)
|
var currentJob = !string.IsNullOrWhiteSpace(streamingRequest.PlaySessionId)
|
||||||
? transcodingJobHelper.GetTranscodingJob(playSessionId)
|
? transcodingJobHelper.GetTranscodingJob(streamingRequest.PlaySessionId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (currentJob != null)
|
if (currentJob != null)
|
||||||
@@ -163,13 +164,13 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
if (mediaSource == null)
|
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[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];
|
mediaSource = mediaSources[0];
|
||||||
}
|
}
|
||||||
@@ -177,7 +178,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(liveStreamId, cancellationToken).ConfigureAwait(false);
|
var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(streamingRequest.LiveStreamId, cancellationToken).ConfigureAwait(false);
|
||||||
mediaSource = liveStreamInfo.Item1;
|
mediaSource = liveStreamInfo.Item1;
|
||||||
state.DirectStreamProvider = liveStreamInfo.Item2;
|
state.DirectStreamProvider = liveStreamInfo.Item2;
|
||||||
}
|
}
|
||||||
@@ -186,28 +187,28 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
var containerInternal = Path.GetExtension(state.RequestedUrl);
|
var containerInternal = Path.GetExtension(state.RequestedUrl);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(container))
|
if (string.IsNullOrEmpty(streamingRequest.Container))
|
||||||
{
|
{
|
||||||
containerInternal = container;
|
containerInternal = streamingRequest.Container;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(containerInternal))
|
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.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);
|
state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
|
||||||
|
|
||||||
if (isVideoRequest)
|
if (state.VideoRequest != null)
|
||||||
{
|
{
|
||||||
state.OutputVideoCodec = state.VideoCodec;
|
state.OutputVideoCodec = state.Request.VideoCodec;
|
||||||
state.OutputVideoBitrate = EncodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
|
state.OutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
|
||||||
|
|
||||||
encodingHelper.TryStreamCopy(state);
|
encodingHelper.TryStreamCopy(state);
|
||||||
|
|
||||||
@@ -220,21 +221,21 @@ namespace Jellyfin.Api.Helpers
|
|||||||
state.OutputVideoBitrate.Value,
|
state.OutputVideoBitrate.Value,
|
||||||
state.VideoStream?.Codec,
|
state.VideoStream?.Codec,
|
||||||
state.OutputVideoCodec,
|
state.OutputVideoCodec,
|
||||||
videoRequest.MaxWidth,
|
state.VideoRequest.MaxWidth,
|
||||||
videoRequest.MaxHeight);
|
state.VideoRequest.MaxHeight);
|
||||||
|
|
||||||
videoRequest.MaxWidth = resolution.MaxWidth;
|
state.VideoRequest.MaxWidth = resolution.MaxWidth;
|
||||||
videoRequest.MaxHeight = resolution.MaxHeight;
|
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)
|
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
|
||||||
? GetOutputFileExtension(state)
|
? GetOutputFileExtension(state)
|
||||||
: ('.' + state.OutputContainer);
|
: ('.' + state.OutputContainer);
|
||||||
|
|
||||||
state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, deviceId, playSessionId);
|
state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -319,7 +320,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The time seek header string.</param>
|
/// <param name="value">The time seek header string.</param>
|
||||||
/// <returns>A nullable <see cref="long"/> representing the seek time in ticks.</returns>
|
/// <returns>A nullable <see cref="long"/> representing the seek time in ticks.</returns>
|
||||||
public static long? ParseTimeSeekHeader(string value)
|
private static long? ParseTimeSeekHeader(string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
{
|
{
|
||||||
@@ -375,7 +376,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="queryString">The query string.</param>
|
/// <param name="queryString">The query string.</param>
|
||||||
/// <returns>A <see cref="Dictionary{String,String}"/> containing the stream options.</returns>
|
/// <returns>A <see cref="Dictionary{String,String}"/> containing the stream options.</returns>
|
||||||
public static Dictionary<string, string> ParseStreamOptions(IQueryCollection queryString)
|
private static Dictionary<string, string> ParseStreamOptions(IQueryCollection queryString)
|
||||||
{
|
{
|
||||||
Dictionary<string, string> streamOptions = new Dictionary<string, string>();
|
Dictionary<string, string> streamOptions = new Dictionary<string, string>();
|
||||||
foreach (var param in queryString)
|
foreach (var param in queryString)
|
||||||
@@ -398,7 +399,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// <param name="state">The current <see cref="StreamState"/>.</param>
|
/// <param name="state">The current <see cref="StreamState"/>.</param>
|
||||||
/// <param name="responseHeaders">The <see cref="IHeaderDictionary"/> of the response.</param>
|
/// <param name="responseHeaders">The <see cref="IHeaderDictionary"/> of the response.</param>
|
||||||
/// <param name="startTimeTicks">The start time in ticks.</param>
|
/// <param name="startTimeTicks">The start time in ticks.</param>
|
||||||
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 runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks!.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture);
|
||||||
var startSeconds = TimeSpan.FromTicks(startTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
|
var startSeconds = TimeSpan.FromTicks(startTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
|
||||||
@@ -420,7 +421,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
public static string? GetOutputFileExtension(StreamState state)
|
private static string? GetOutputFileExtension(StreamState state)
|
||||||
{
|
{
|
||||||
var ext = Path.GetExtension(state.RequestedUrl);
|
var ext = Path.GetExtension(state.RequestedUrl);
|
||||||
|
|
||||||
@@ -432,7 +433,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
// Try to infer based on the desired video codec
|
// Try to infer based on the desired video codec
|
||||||
if (state.IsVideoRequest)
|
if (state.IsVideoRequest)
|
||||||
{
|
{
|
||||||
var videoCodec = state.VideoCodec;
|
var videoCodec = state.Request.VideoCodec;
|
||||||
|
|
||||||
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) ||
|
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(videoCodec, "h265", 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
|
// Try to infer based on the desired audio codec
|
||||||
if (!state.IsVideoRequest)
|
if (!state.IsVideoRequest)
|
||||||
{
|
{
|
||||||
var audioCodec = state.AudioCodec;
|
var audioCodec = state.Request.AudioCodec;
|
||||||
|
|
||||||
if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@@ -570,7 +571,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
// state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
|
// state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
|
||||||
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
|
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
|
||||||
|
|
||||||
if (!state.IsVideoRequest)
|
if (state.VideoRequest != null)
|
||||||
{
|
{
|
||||||
state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
|
state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
|
||||||
state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
|
state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
|
||||||
@@ -583,11 +584,16 @@ namespace Jellyfin.Api.Helpers
|
|||||||
/// Parses the parameters.
|
/// Parses the parameters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
private void ParseParams(StreamRequest request)
|
private static void ParseParams(StreamingRequestDto request)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(request.Params))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var vals = request.Params.Split(';');
|
var vals = request.Params.Split(';');
|
||||||
|
|
||||||
var videoRequest = request as VideoStreamRequest;
|
var videoRequest = request as VideoRequestDto;
|
||||||
|
|
||||||
for (var i = 0; i < vals.Length; i++)
|
for (var i = 0; i < vals.Length; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
job.BitRate = bitRate;
|
job.BitRate = bitRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
var deviceId = state.DeviceId;
|
var deviceId = state.Request.DeviceId;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(deviceId))
|
if (!string.IsNullOrWhiteSpace(deviceId))
|
||||||
{
|
{
|
||||||
@@ -486,7 +486,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
HttpRequest request,
|
HttpRequest request,
|
||||||
TranscodingJobType transcodingJobType,
|
TranscodingJobType transcodingJobType,
|
||||||
CancellationTokenSource cancellationTokenSource,
|
CancellationTokenSource cancellationTokenSource,
|
||||||
string workingDirectory = null)
|
string? workingDirectory = null)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||||
|
|
||||||
@@ -525,12 +525,12 @@ namespace Jellyfin.Api.Helpers
|
|||||||
|
|
||||||
var transcodingJob = this.OnTranscodeBeginning(
|
var transcodingJob = this.OnTranscodeBeginning(
|
||||||
outputPath,
|
outputPath,
|
||||||
state.PlaySessionId,
|
state.Request.PlaySessionId,
|
||||||
state.MediaSource.LiveStreamId,
|
state.MediaSource.LiveStreamId,
|
||||||
Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
|
Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
|
||||||
transcodingJobType,
|
transcodingJobType,
|
||||||
process,
|
process,
|
||||||
state.DeviceId,
|
state.Request.DeviceId,
|
||||||
state,
|
state,
|
||||||
cancellationTokenSource);
|
cancellationTokenSource);
|
||||||
|
|
||||||
@@ -706,9 +706,9 @@ namespace Jellyfin.Api.Helpers
|
|||||||
_transcodingLocks.Remove(path);
|
_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);
|
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(
|
var liveStreamResponse = await _mediaSourceManager.OpenLiveStream(
|
||||||
new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken },
|
new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken },
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Jellyfin.Api.Models.StreamingDtos
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="StreamState" /> class.
|
/// Initializes a new instance of the <see cref="StreamState" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mediaSourceManager">Instance of the <see cref="mediaSourceManager" /> interface.</param>
|
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager" /> interface.</param>
|
||||||
/// <param name="transcodingType">The <see cref="TranscodingJobType" />.</param>
|
/// <param name="transcodingType">The <see cref="TranscodingJobType" />.</param>
|
||||||
/// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper" /> singleton.</param>
|
/// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper" /> singleton.</param>
|
||||||
public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, TranscodingJobHelper transcodingJobHelper)
|
public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, TranscodingJobHelper transcodingJobHelper)
|
||||||
@@ -34,29 +34,28 @@ namespace Jellyfin.Api.Models.StreamingDtos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? RequestedUrl { get; set; }
|
public string? RequestedUrl { get; set; }
|
||||||
|
|
||||||
// /// <summary>
|
/// <summary>
|
||||||
// /// Gets or sets the request.
|
/// Gets or sets the request.
|
||||||
// /// </summary>
|
/// </summary>
|
||||||
// public StreamRequest Request
|
public StreamingRequestDto Request
|
||||||
// {
|
{
|
||||||
// get => (StreamRequest)BaseRequest;
|
get => (StreamingRequestDto)BaseRequest;
|
||||||
// set
|
set
|
||||||
// {
|
{
|
||||||
// BaseRequest = value;
|
BaseRequest = value;
|
||||||
//
|
IsVideoRequest = VideoRequest != null;
|
||||||
// IsVideoRequest = VideoRequest != null;
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the transcoding throttler.
|
/// Gets or sets the transcoding throttler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TranscodingThrottler? TranscodingThrottler { get; set; }
|
public TranscodingThrottler? TranscodingThrottler { get; set; }
|
||||||
|
|
||||||
/*/// <summary>
|
/// <summary>
|
||||||
/// Gets the video request.
|
/// Gets the video request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public VideoStreamRequest VideoRequest => Request as VideoStreamRequest;*/
|
public VideoRequestDto? VideoRequest => Request! as VideoRequestDto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the direct stream provicer.
|
/// Gets or sets the direct stream provicer.
|
||||||
@@ -68,10 +67,10 @@ namespace Jellyfin.Api.Models.StreamingDtos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? WaitForPath { get; set; }
|
public string? WaitForPath { get; set; }
|
||||||
|
|
||||||
/*/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the request outputs video.
|
/// Gets a value indicating whether the request outputs video.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsOutputVideo => Request is VideoStreamRequest;*/
|
public bool IsOutputVideo => Request is VideoRequestDto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the segment length.
|
/// Gets the segment length.
|
||||||
@@ -161,36 +160,6 @@ namespace Jellyfin.Api.Models.StreamingDtos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public TranscodingJobDto? TranscodingJob { get; set; }
|
public TranscodingJobDto? TranscodingJob { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the device id.
|
|
||||||
/// </summary>
|
|
||||||
public string? DeviceId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the play session id.
|
|
||||||
/// </summary>
|
|
||||||
public string? PlaySessionId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the live stream id.
|
|
||||||
/// </summary>
|
|
||||||
public string? LiveStreamId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the video coded.
|
|
||||||
/// </summary>
|
|
||||||
public string? VideoCodec { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the audio codec.
|
|
||||||
/// </summary>
|
|
||||||
public string? AudioCodec { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the subtitle codec.
|
|
||||||
/// </summary>
|
|
||||||
public string? SubtitleCodec { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -219,7 +188,7 @@ namespace Jellyfin.Api.Models.StreamingDtos
|
|||||||
{
|
{
|
||||||
// REVIEW: Is this the right place for this?
|
// REVIEW: Is this the right place for this?
|
||||||
if (MediaSource.RequiresClosing
|
if (MediaSource.RequiresClosing
|
||||||
&& string.IsNullOrWhiteSpace(LiveStreamId)
|
&& string.IsNullOrWhiteSpace(Request.LiveStreamId)
|
||||||
&& !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
|
&& !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
|
||||||
{
|
{
|
||||||
_mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).GetAwaiter().GetResult();
|
_mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).GetAwaiter().GetResult();
|
||||||
|
|||||||
45
Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs
Normal file
45
Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Models.StreamingDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The audio streaming request dto.
|
||||||
|
/// </summary>
|
||||||
|
public class StreamingRequestDto : BaseEncodingJobOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the device profile.
|
||||||
|
/// </summary>
|
||||||
|
public string? DeviceProfileId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the params.
|
||||||
|
/// </summary>
|
||||||
|
public string? Params { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the play session id.
|
||||||
|
/// </summary>
|
||||||
|
public string? PlaySessionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the tag.
|
||||||
|
/// </summary>
|
||||||
|
public string? Tag { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the segment container.
|
||||||
|
/// </summary>
|
||||||
|
public string? SegmentContainer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the segment length.
|
||||||
|
/// </summary>
|
||||||
|
public int? SegmentLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the min segments.
|
||||||
|
/// </summary>
|
||||||
|
public int? MinSegments { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs
Normal file
19
Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Jellyfin.Api.Models.StreamingDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The video request dto.
|
||||||
|
/// </summary>
|
||||||
|
public class VideoRequestDto : StreamingRequestDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this instance has fixed resolution.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value>
|
||||||
|
public bool HasFixedResolution => Width.HasValue || Height.HasValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to enable subtitles in the manifest.
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableSubtitlesInManifest { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user