Remove DLNA API code

pull/10558/head
Patrick Barron 6 months ago
parent 0a03539dc4
commit 01fd42cf95

@ -1,25 +0,0 @@
using Emby.Dlna;
using MediaBrowser.Controller.Configuration;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace Jellyfin.Api.Attributes;
/// <inheritdoc />
public sealed class DlnaEnabledAttribute : ActionFilterAttribute
{
/// <inheritdoc />
public override void OnActionExecuting(ActionExecutingContext context)
{
var serverConfigurationManager = context.HttpContext.RequestServices.GetRequiredService<IServerConfigurationManager>();
var enabled = serverConfigurationManager.GetDlnaConfiguration().EnableServer;
if (!enabled)
{
context.Result = new StatusCodeResult(StatusCodes.Status503ServiceUnavailable);
}
}
}

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Routing;
namespace Jellyfin.Api.Attributes;
/// <summary>
/// Identifies an action that supports the HTTP GET method.
/// </summary>
public sealed class HttpSubscribeAttribute : HttpMethodAttribute
{
private static readonly IEnumerable<string> _supportedMethods = new[] { "SUBSCRIBE" };
/// <summary>
/// Initializes a new instance of the <see cref="HttpSubscribeAttribute"/> class.
/// </summary>
public HttpSubscribeAttribute()
: base(_supportedMethods)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpSubscribeAttribute"/> class.
/// </summary>
/// <param name="template">The route template. May not be null.</param>
public HttpSubscribeAttribute(string template)
: base(_supportedMethods, template)
=> ArgumentNullException.ThrowIfNull(template);
}

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Routing;
namespace Jellyfin.Api.Attributes;
/// <summary>
/// Identifies an action that supports the HTTP GET method.
/// </summary>
public sealed class HttpUnsubscribeAttribute : HttpMethodAttribute
{
private static readonly IEnumerable<string> _supportedMethods = new[] { "UNSUBSCRIBE" };
/// <summary>
/// Initializes a new instance of the <see cref="HttpUnsubscribeAttribute"/> class.
/// </summary>
public HttpUnsubscribeAttribute()
: base(_supportedMethods)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpUnsubscribeAttribute"/> class.
/// </summary>
/// <param name="template">The route template. May not be null.</param>
public HttpUnsubscribeAttribute(string template)
: base(_supportedMethods, template)
=> ArgumentNullException.ThrowIfNull(template);
}

@ -15,7 +15,6 @@ namespace Jellyfin.Api.Controllers;
/// <summary>
/// The audio controller.
/// </summary>
// TODO: In order to authenticate this in the future, Dlna playback will require updating
public class AudioController : BaseJellyfinApiController
{
private readonly AudioHelper _audioHelper;
@ -95,7 +94,7 @@ public class AudioController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -147,7 +146,6 @@ public class AudioController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -260,7 +258,7 @@ public class AudioController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -312,7 +310,6 @@ public class AudioController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,

@ -1,133 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Jellyfin.Api.Constants;
using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Dlna;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers;
/// <summary>
/// Dlna Controller.
/// </summary>
[Authorize(Policy = Policies.RequiresElevation)]
public class DlnaController : BaseJellyfinApiController
{
private readonly IDlnaManager _dlnaManager;
/// <summary>
/// Initializes a new instance of the <see cref="DlnaController"/> class.
/// </summary>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
public DlnaController(IDlnaManager dlnaManager)
{
_dlnaManager = dlnaManager;
}
/// <summary>
/// Get profile infos.
/// </summary>
/// <response code="200">Device profile infos returned.</response>
/// <returns>An <see cref="OkResult"/> containing the device profile infos.</returns>
[HttpGet("ProfileInfos")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<DeviceProfileInfo>> GetProfileInfos()
{
return Ok(_dlnaManager.GetProfileInfos());
}
/// <summary>
/// Gets the default profile.
/// </summary>
/// <response code="200">Default device profile returned.</response>
/// <returns>An <see cref="OkResult"/> containing the default profile.</returns>
[HttpGet("Profiles/Default")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<DeviceProfile> GetDefaultProfile()
{
return _dlnaManager.GetDefaultProfile();
}
/// <summary>
/// Gets a single profile.
/// </summary>
/// <param name="profileId">Profile Id.</param>
/// <response code="200">Device profile returned.</response>
/// <response code="404">Device profile not found.</response>
/// <returns>An <see cref="OkResult"/> containing the profile on success, or a <see cref="NotFoundResult"/> if device profile not found.</returns>
[HttpGet("Profiles/{profileId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<DeviceProfile> GetProfile([FromRoute, Required] string profileId)
{
var profile = _dlnaManager.GetProfile(profileId);
if (profile is null)
{
return NotFound();
}
return profile;
}
/// <summary>
/// Deletes a profile.
/// </summary>
/// <param name="profileId">Profile id.</param>
/// <response code="204">Device profile deleted.</response>
/// <response code="404">Device profile not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if profile not found.</returns>
[HttpDelete("Profiles/{profileId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult DeleteProfile([FromRoute, Required] string profileId)
{
var existingDeviceProfile = _dlnaManager.GetProfile(profileId);
if (existingDeviceProfile is null)
{
return NotFound();
}
_dlnaManager.DeleteProfile(profileId);
return NoContent();
}
/// <summary>
/// Creates a profile.
/// </summary>
/// <param name="deviceProfile">Device profile.</param>
/// <response code="204">Device profile created.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Profiles")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CreateProfile([FromBody] DeviceProfile deviceProfile)
{
_dlnaManager.CreateProfile(deviceProfile);
return NoContent();
}
/// <summary>
/// Updates a profile.
/// </summary>
/// <param name="profileId">Profile id.</param>
/// <param name="deviceProfile">Device profile.</param>
/// <response code="204">Device profile updated.</response>
/// <response code="404">Device profile not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if profile not found.</returns>
[HttpPost("Profiles/{profileId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateProfile([FromRoute, Required] string profileId, [FromBody] DeviceProfile deviceProfile)
{
var existingDeviceProfile = _dlnaManager.GetProfile(profileId);
if (existingDeviceProfile is null)
{
return NotFound();
}
_dlnaManager.UpdateProfile(profileId, deviceProfile);
return NoContent();
}
}

@ -1,330 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Mime;
using System.Threading.Tasks;
using Emby.Dlna;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers;
/// <summary>
/// Dlna Server Controller.
/// </summary>
[Route("Dlna")]
[DlnaEnabled]
[Authorize(Policy = Policies.AnonymousLanAccessPolicy)]
public class DlnaServerController : BaseJellyfinApiController
{
private readonly IDlnaManager _dlnaManager;
private readonly IContentDirectory _contentDirectory;
private readonly IConnectionManager _connectionManager;
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
/// <summary>
/// Initializes a new instance of the <see cref="DlnaServerController"/> class.
/// </summary>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="contentDirectory">Instance of the <see cref="IContentDirectory"/> interface.</param>
/// <param name="connectionManager">Instance of the <see cref="IConnectionManager"/> interface.</param>
/// <param name="mediaReceiverRegistrar">Instance of the <see cref="IMediaReceiverRegistrar"/> interface.</param>
public DlnaServerController(
IDlnaManager dlnaManager,
IContentDirectory contentDirectory,
IConnectionManager connectionManager,
IMediaReceiverRegistrar mediaReceiverRegistrar)
{
_dlnaManager = dlnaManager;
_contentDirectory = contentDirectory;
_connectionManager = connectionManager;
_mediaReceiverRegistrar = mediaReceiverRegistrar;
}
/// <summary>
/// Get Description Xml.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Description xml returned.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>An <see cref="OkResult"/> containing the description xml.</returns>
[HttpGet("{serverId}/description")]
[HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<string> GetDescriptionXml([FromRoute, Required] string serverId)
{
var url = GetAbsoluteUri();
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress);
return Ok(xml);
}
/// <summary>
/// Gets Dlna content directory xml.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Dlna content directory returned.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>An <see cref="OkResult"/> containing the dlna content directory xml.</returns>
[HttpGet("{serverId}/ContentDirectory")]
[HttpGet("{serverId}/ContentDirectory/ContentDirectory", Name = "GetContentDirectory_2")]
[HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_3")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
public ActionResult<string> GetContentDirectory([FromRoute, Required] string serverId)
{
return Ok(_contentDirectory.GetServiceXml());
}
/// <summary>
/// Gets Dlna media receiver registrar xml.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Dlna media receiver registrar xml returned.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Dlna media receiver registrar xml.</returns>
[HttpGet("{serverId}/MediaReceiverRegistrar")]
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar", Name = "GetMediaReceiverRegistrar_2")]
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_3")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
public ActionResult<string> GetMediaReceiverRegistrar([FromRoute, Required] string serverId)
{
return Ok(_mediaReceiverRegistrar.GetServiceXml());
}
/// <summary>
/// Gets Dlna media receiver registrar xml.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Dlna media receiver registrar xml returned.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Dlna media receiver registrar xml.</returns>
[HttpGet("{serverId}/ConnectionManager")]
[HttpGet("{serverId}/ConnectionManager/ConnectionManager", Name = "GetConnectionManager_2")]
[HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_3")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
public ActionResult<string> GetConnectionManager([FromRoute, Required] string serverId)
{
return Ok(_connectionManager.GetServiceXml());
}
/// <summary>
/// Process a content directory control request.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Request processed.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Control response.</returns>
[HttpPost("{serverId}/ContentDirectory/Control")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public async Task<ActionResult<ControlResponse>> ProcessContentDirectoryControlRequest([FromRoute, Required] string serverId)
{
return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false);
}
/// <summary>
/// Process a connection manager control request.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Request processed.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Control response.</returns>
[HttpPost("{serverId}/ConnectionManager/Control")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public async Task<ActionResult<ControlResponse>> ProcessConnectionManagerControlRequest([FromRoute, Required] string serverId)
{
return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false);
}
/// <summary>
/// Process a media receiver registrar control request.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Request processed.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Control response.</returns>
[HttpPost("{serverId}/MediaReceiverRegistrar/Control")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public async Task<ActionResult<ControlResponse>> ProcessMediaReceiverRegistrarControlRequest([FromRoute, Required] string serverId)
{
return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false);
}
/// <summary>
/// Processes an event subscription request.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Request processed.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Event subscription response.</returns>
[HttpSubscribe("{serverId}/MediaReceiverRegistrar/Events")]
[HttpUnsubscribe("{serverId}/MediaReceiverRegistrar/Events")]
[ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<EventSubscriptionResponse> ProcessMediaReceiverRegistrarEventRequest(string serverId)
{
return ProcessEventRequest(_mediaReceiverRegistrar);
}
/// <summary>
/// Processes an event subscription request.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Request processed.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Event subscription response.</returns>
[HttpSubscribe("{serverId}/ContentDirectory/Events")]
[HttpUnsubscribe("{serverId}/ContentDirectory/Events")]
[ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<EventSubscriptionResponse> ProcessContentDirectoryEventRequest(string serverId)
{
return ProcessEventRequest(_contentDirectory);
}
/// <summary>
/// Processes an event subscription request.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <response code="200">Request processed.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Event subscription response.</returns>
[HttpSubscribe("{serverId}/ConnectionManager/Events")]
[HttpUnsubscribe("{serverId}/ConnectionManager/Events")]
[ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[Produces(MediaTypeNames.Text.Xml)]
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<EventSubscriptionResponse> ProcessConnectionManagerEventRequest(string serverId)
{
return ProcessEventRequest(_connectionManager);
}
/// <summary>
/// Gets a server icon.
/// </summary>
/// <param name="serverId">Server UUID.</param>
/// <param name="fileName">The icon filename.</param>
/// <response code="200">Request processed.</response>
/// <response code="404">Not Found.</response>
/// <response code="503">DLNA is disabled.</response>
/// <returns>Icon stream.</returns>
[HttpGet("{serverId}/icons/{fileName}")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[ProducesImageFile]
public ActionResult GetIconId([FromRoute, Required] string serverId, [FromRoute, Required] string fileName)
{
return GetIconInternal(fileName);
}
/// <summary>
/// Gets a server icon.
/// </summary>
/// <param name="fileName">The icon filename.</param>
/// <returns>Icon stream.</returns>
/// <response code="200">Request processed.</response>
/// <response code="404">Not Found.</response>
/// <response code="503">DLNA is disabled.</response>
[HttpGet("icons/{fileName}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
[ProducesImageFile]
public ActionResult GetIcon([FromRoute, Required] string fileName)
{
return GetIconInternal(fileName);
}
private ActionResult GetIconInternal(string fileName)
{
var icon = _dlnaManager.GetIcon(fileName);
if (icon is null)
{
return NotFound();
}
return File(icon.Stream, MimeTypes.GetMimeType(fileName));
}
private string GetAbsoluteUri()
{
return $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Request.Path}";
}
private Task<ControlResponse> ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service)
{
return service.ProcessControlRequestAsync(new ControlRequest(Request.Headers)
{
InputXml = requestStream,
TargetServerUuId = id,
RequestedUrl = GetAbsoluteUri()
});
}
private EventSubscriptionResponse ProcessEventRequest(IDlnaEventManager dlnaEventManager)
{
var subscriptionId = Request.Headers["SID"];
if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase))
{
var notificationType = Request.Headers["NT"];
var callback = Request.Headers["CALLBACK"];
var timeoutString = Request.Headers["TIMEOUT"];
if (string.IsNullOrEmpty(notificationType))
{
return dlnaEventManager.RenewEventSubscription(
subscriptionId,
notificationType,
timeoutString,
callback);
}
return dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callback);
}
return dlnaEventManager.CancelEventSubscription(subscriptionId);
}
}

@ -17,8 +17,6 @@ using Jellyfin.Extensions;
using Jellyfin.MediaEncoding.Hls.Playlist;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Encoder;
@ -49,12 +47,10 @@ public class DynamicHlsController : BaseJellyfinApiController
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDlnaManager _dlnaManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly ILogger<DynamicHlsController> _logger;
private readonly EncodingHelper _encodingHelper;
@ -67,12 +63,10 @@ public class DynamicHlsController : BaseJellyfinApiController
/// </summary>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> 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="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
/// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</param>
@ -81,12 +75,10 @@ public class DynamicHlsController : BaseJellyfinApiController
public DynamicHlsController(
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
IFileSystem fileSystem,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
ILogger<DynamicHlsController> logger,
DynamicHlsHelper dynamicHlsHelper,
@ -95,12 +87,10 @@ public class DynamicHlsController : BaseJellyfinApiController
{
_libraryManager = libraryManager;
_userManager = userManager;
_dlnaManager = dlnaManager;
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
_fileSystem = fileSystem;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_logger = logger;
_dynamicHlsHelper = dynamicHlsHelper;
@ -176,7 +166,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -231,7 +221,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -294,8 +283,6 @@ public class DynamicHlsController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
TranscodingJobType,
cancellationToken)
@ -422,7 +409,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -477,7 +464,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -594,7 +580,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -647,7 +633,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -760,7 +745,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -814,7 +799,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -928,7 +912,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -981,7 +965,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -1105,7 +1088,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -1161,7 +1144,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -1286,7 +1268,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -1341,7 +1323,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -1402,8 +1383,6 @@ public class DynamicHlsController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
TranscodingJobType,
cancellationTokenSource.Token)
@ -1442,8 +1421,6 @@ public class DynamicHlsController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
TranscodingJobType,
cancellationToken)

@ -16,8 +16,6 @@ using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@ -42,11 +40,9 @@ public class VideosController : BaseJellyfinApiController
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDtoService _dtoService;
private readonly IDlnaManager _dlnaManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly IHttpClientFactory _httpClientFactory;
private readonly EncodingHelper _encodingHelper;
@ -59,11 +55,9 @@ public class VideosController : BaseJellyfinApiController
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> 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="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
@ -71,11 +65,9 @@ public class VideosController : BaseJellyfinApiController
ILibraryManager libraryManager,
IUserManager userManager,
IDtoService dtoService,
IDlnaManager dlnaManager,
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
IHttpClientFactory httpClientFactory,
EncodingHelper encodingHelper)
@ -83,11 +75,9 @@ public class VideosController : BaseJellyfinApiController
_libraryManager = libraryManager;
_userManager = userManager;
_dtoService = dtoService;
_dlnaManager = dlnaManager;
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_httpClientFactory = httpClientFactory;
_encodingHelper = encodingHelper;
@ -324,7 +314,7 @@ public class VideosController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
[FromQuery] string? deviceProfileId,
[FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@ -381,7 +371,6 @@ public class VideosController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@ -438,8 +427,6 @@ public class VideosController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
_transcodingJobType,
cancellationTokenSource.Token)
@ -447,8 +434,6 @@ public class VideosController : BaseJellyfinApiController
if (@static.HasValue && @static.Value && state.DirectStreamProvider is not null)
{
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfo(streamingRequest.LiveStreamId);
if (liveStreamInfo is null)
{
@ -463,8 +448,6 @@ public class VideosController : BaseJellyfinApiController
// Static remote stream
if (@static.HasValue && @static.Value && state.InputProtocol == MediaProtocol.Http)
{
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, httpClient, HttpContext).ConfigureAwait(false);
}
@ -475,12 +458,6 @@ public class VideosController : BaseJellyfinApiController
}
var outputPath = state.OutputFilePath;
var outputPathExists = System.IO.File.Exists(outputPath);
var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
var isTranscodeCached = outputPathExists && transcodingJob is not null;
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, (@static.HasValue && @static.Value) || isTranscodeCached, state.Request.StartTimeTicks, Request, _dlnaManager);
// Static stream
if (@static.HasValue && @static.Value)

@ -7,8 +7,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.MediaInfo;
@ -23,13 +21,11 @@ namespace Jellyfin.Api.Helpers;
/// </summary>
public class AudioHelper
{
private readonly IDlnaManager _dlnaManager;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHttpContextAccessor _httpContextAccessor;
@ -38,37 +34,31 @@ public class AudioHelper
/// <summary>
/// Initializes a new instance of the <see cref="AudioHelper"/> class.
/// </summary>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> 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="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> 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="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
public AudioHelper(
IDlnaManager dlnaManager,
IUserManager userManager,
ILibraryManager libraryManager,
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
IHttpClientFactory httpClientFactory,
IHttpContextAccessor httpContextAccessor,
EncodingHelper encodingHelper)
{
_dlnaManager = dlnaManager;
_userManager = userManager;
_libraryManager = libraryManager;
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_httpClientFactory = httpClientFactory;
_httpContextAccessor = httpContextAccessor;
@ -104,8 +94,6 @@ public class AudioHelper
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
transcodingJobType,
cancellationTokenSource.Token)
@ -113,8 +101,6 @@ public class AudioHelper
if (streamingRequest.Static && state.DirectStreamProvider is not null)
{
StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, true, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager);
var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfo(streamingRequest.LiveStreamId);
if (liveStreamInfo is null)
{
@ -129,8 +115,6 @@ public class AudioHelper
// Static remote stream
if (streamingRequest.Static && state.InputProtocol == MediaProtocol.Http)
{
StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, true, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager);
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, httpClient, _httpContextAccessor.HttpContext).ConfigureAwait(false);
}
@ -141,12 +125,6 @@ public class AudioHelper
}
var outputPath = state.OutputFilePath;
var outputPathExists = File.Exists(outputPath);
var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
var isTranscodeCached = outputPathExists && transcodingJob is not null;
StreamingHelpers.AddDlnaHeaders(state, _httpContextAccessor.HttpContext.Response.Headers, streamingRequest.Static || isTranscodeCached, streamingRequest.StartTimeTicks, _httpContextAccessor.HttpContext.Request, _dlnaManager);
// Static stream
if (streamingRequest.Static)

@ -16,8 +16,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Trickplay;
@ -38,11 +36,9 @@ public class DynamicHlsHelper
{
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDlnaManager _dlnaManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly INetworkManager _networkManager;
private readonly ILogger<DynamicHlsHelper> _logger;
@ -55,11 +51,9 @@ public class DynamicHlsHelper
/// </summary>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> 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="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsHelper}"/> interface.</param>
@ -69,11 +63,9 @@ public class DynamicHlsHelper
public DynamicHlsHelper(
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
INetworkManager networkManager,
ILogger<DynamicHlsHelper> logger,
@ -83,11 +75,9 @@ public class DynamicHlsHelper
{
_libraryManager = libraryManager;
_userManager = userManager;
_dlnaManager = dlnaManager;
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
_deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_networkManager = networkManager;
_logger = logger;
@ -140,8 +130,6 @@ public class DynamicHlsHelper
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
_dlnaManager,
_deviceManager,
_transcodingJobHelper,
transcodingJobType,
cancellationTokenSource.Token)

@ -12,15 +12,12 @@ using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Jellyfin.Api.Helpers;
@ -41,8 +38,6 @@ public static class StreamingHelpers
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</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>
@ -56,21 +51,11 @@ public static class StreamingHelpers
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
EncodingHelper encodingHelper,
IDlnaManager dlnaManager,
IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
TranscodingJobType transcodingJobType,
CancellationToken cancellationToken)
{
var httpRequest = httpContext.Request;
// Parse the DLNA time seek header
if (!streamingRequest.StartTimeTicks.HasValue)
{
var timeSeek = httpRequest.Headers["TimeSeekRange.dlna.org"];
streamingRequest.StartTimeTicks = ParseTimeSeekHeader(timeSeek.ToString());
}
if (!string.IsNullOrWhiteSpace(streamingRequest.Params))
{
ParseParams(streamingRequest);
@ -89,16 +74,11 @@ public static class StreamingHelpers
streamingRequest.AudioCodec = encodingHelper.InferAudioCodec(url);
}
var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) ||
streamingRequest.StreamOptions.ContainsKey("dlnaheaders") ||
string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
{
Request = streamingRequest,
RequestedUrl = url,
UserAgent = httpRequest.Headers[HeaderNames.UserAgent],
EnableDlnaHeaders = enableDlnaHeaders
UserAgent = httpRequest.Headers[HeaderNames.UserAgent]
};
var userId = httpContext.User.GetUserId();
@ -243,8 +223,6 @@ public static class StreamingHelpers
}
}
ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static);
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
? GetOutputFileExtension(state, mediaSource)
: ("." + state.OutputContainer);
@ -254,123 +232,6 @@ public static class StreamingHelpers
return state;
}
/// <summary>
/// Adds the dlna headers.
/// </summary>
/// <param name="state">The state.</param>
/// <param name="responseHeaders">The response headers.</param>
/// <param name="isStaticallyStreamed">if set to <c>true</c> [is statically streamed].</param>
/// <param name="startTimeTicks">The start time in ticks.</param>
/// <param name="request">The <see cref="HttpRequest"/>.</param>
/// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
public static void AddDlnaHeaders(
StreamState state,
IHeaderDictionary responseHeaders,
bool isStaticallyStreamed,
long? startTimeTicks,
HttpRequest request,
IDlnaManager dlnaManager)
{
if (!state.EnableDlnaHeaders)
{
return;
}
var profile = state.DeviceProfile;
StringValues transferMode = request.Headers["transferMode.dlna.org"];
responseHeaders.Append("transferMode.dlna.org", string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode.ToString());
responseHeaders.Append("realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*");
if (state.RunTimeTicks.HasValue)
{
if (string.Equals(request.Headers["getMediaInfo.sec"], "1", StringComparison.OrdinalIgnoreCase))
{
var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
responseHeaders.Append("MediaInfo.sec", string.Format(
CultureInfo.InvariantCulture,
"SEC_Duration={0};",
Convert.ToInt32(ms)));
}
if (!isStaticallyStreamed && profile is not null)
{
AddTimeSeekResponseHeaders(state, responseHeaders, startTimeTicks);
}
}
profile ??= dlnaManager.GetDefaultProfile();
var audioCodec = state.ActualOutputAudioCodec;
if (!state.IsVideoRequest)
{
responseHeaders.Append("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader(
profile,
state.OutputContainer,
audioCodec,
state.OutputAudioBitrate,
state.OutputAudioSampleRate,
state.OutputAudioChannels,
state.OutputAudioBitDepth,
isStaticallyStreamed,
state.RunTimeTicks,
state.TranscodeSeekInfo));
}
else
{
var videoCodec = state.ActualOutputVideoCodec;
responseHeaders.Append(
"contentFeatures.dlna.org",
ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoRangeType, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty);
}
}
/// <summary>
/// Parses the time seek header.
/// </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(ReadOnlySpan<char> value)
{
if (value.IsEmpty)
{
return null;
}
const string npt = "npt=";
if (!value.StartsWith(npt, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Invalid timeseek header");
}
var index = value.IndexOf('-');
value = index == -1
? value.Slice(npt.Length)
: value.Slice(npt.Length, index - npt.Length);
if (!value.Contains(':'))
{
// Parses npt times in the format of '417.33'
if (double.TryParse(value, CultureInfo.InvariantCulture, out var seconds))
{
return TimeSpan.FromSeconds(seconds).Ticks;
}
throw new ArgumentException("Invalid timeseek header");
}
try
{
// Parses npt times in the format of '10:19:25.7'
return TimeSpan.Parse(value, CultureInfo.InvariantCulture).Ticks;
}
catch
{
throw new ArgumentException("Invalid timeseek header");
}
}
/// <summary>
/// Parses query parameters as StreamOptions.
/// </summary>
@ -393,29 +254,6 @@ public static class StreamingHelpers
return streamOptions;
}
/// <summary>
/// Adds the dlna time seek headers to the response.
/// </summary>
/// <param name="state">The current <see cref="StreamState"/>.</param>
/// <param name="responseHeaders">The <see cref="IHeaderDictionary"/> of the response.</param>
/// <param name="startTimeTicks">The start time in ticks.</param>
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);
responseHeaders.Append("TimeSeekRange.dlna.org", string.Format(
CultureInfo.InvariantCulture,
"npt={0}-{1}/{1}",
startSeconds,
runtimeSeconds));
responseHeaders.Append("X-AvailableSeekRange", string.Format(
CultureInfo.InvariantCulture,
"1 npt={0}-{1}",
startSeconds,
runtimeSeconds));
}
/// <summary>
/// Gets the output file extension.
/// </summary>
@ -519,79 +357,6 @@ public static class StreamingHelpers
return Path.Combine(folder, filename + ext);
}
private static void ApplyDeviceProfileSettings(StreamState state, IDlnaManager dlnaManager, IDeviceManager deviceManager, HttpRequest request, string? deviceProfileId, bool? @static)
{
if (!string.IsNullOrWhiteSpace(deviceProfileId))
{
state.DeviceProfile = dlnaManager.GetProfile(deviceProfileId);
if (state.DeviceProfile is null)
{
var caps = deviceManager.GetCapabilities(deviceProfileId);
state.DeviceProfile = caps is null ? dlnaManager.GetProfile(request.Headers) : caps.DeviceProfile;
}
}
var profile = state.DeviceProfile;
if (profile is null)
{
// Don't use settings from the default profile.
// Only use a specific profile if it was requested.
return;
}
var audioCodec = state.ActualOutputAudioCodec;
var videoCodec = state.ActualOutputVideoCodec;
var mediaProfile = !state.IsVideoRequest
? profile.GetAudioMediaProfile(state.OutputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate, state.OutputAudioSampleRate, state.OutputAudioBitDepth)
: profile.GetVideoMediaProfile(
state.OutputContainer,
audioCodec,
videoCodec,
state.OutputWidth,
state.OutputHeight,
state.TargetVideoBitDepth,
state.OutputVideoBitrate,
state.TargetVideoProfile,
state.TargetVideoRangeType,
state.TargetVideoLevel,
state.TargetFramerate,
state.TargetPacketLength,
state.TargetTimestamp,
state.IsTargetAnamorphic,
state.IsTargetInterlaced,
state.TargetRefFrames,
state.TargetVideoStreamCount,
state.TargetAudioStreamCount,
state.TargetVideoCodecTag,
state.IsTargetAVC);
if (mediaProfile is not null)
{
state.MimeType = mediaProfile.MimeType;
}
if (!(@static.HasValue && @static.Value))
{
var transcodingProfile = !state.IsVideoRequest ? profile.GetAudioTranscodingProfile(state.OutputContainer, audioCodec) : profile.GetVideoTranscodingProfile(state.OutputContainer, audioCodec, videoCodec);
if (transcodingProfile is not null)
{
state.EstimateContentLength = transcodingProfile.EstimateContentLength;
// state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
if (state.VideoRequest is not null)
{
state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
}
}
}
}
/// <summary>
/// Parses the parameters.
/// </summary>
@ -619,7 +384,7 @@ public static class StreamingHelpers
switch (i)
{
case 0:
request.DeviceProfileId = val;
// DeviceProfileId
break;
case 1:
request.DeviceId = val;

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
<ProjectReference Include="..\Jellyfin.Networking\Jellyfin.Networking.csproj" />
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj" />
<ProjectReference Include="..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" />

@ -138,16 +138,6 @@ public class StreamState : EncodingJobInfo, IDisposable
/// </summary>
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to enable dlna headers.
/// </summary>
public bool EnableDlnaHeaders { get; set; }
/// <summary>
/// Gets or sets the device profile.
/// </summary>
public DeviceProfile? DeviceProfile { get; set; }
/// <summary>
/// Gets or sets the transcoding job.
/// </summary>

@ -7,11 +7,6 @@ namespace Jellyfin.Api.Models.StreamingDtos;
/// </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>

Loading…
Cancel
Save