Add Dlna device profile code

This commit is contained in:
Patrick Barron 2024-01-12 14:33:30 -05:00
parent ce934aae8e
commit 53142d6743
28 changed files with 659 additions and 58 deletions

View File

@ -13,7 +13,7 @@ namespace Jellyfin.Plugin.Dlna.Model;
public static class ContentFeatureBuilder
{
public static string BuildImageHeader(
DeviceProfile profile,
DlnaDeviceProfile profile,
string container,
int? width,
int? height,
@ -58,7 +58,7 @@ public static class ContentFeatureBuilder
}
public static string BuildAudioHeader(
DeviceProfile profile,
DlnaDeviceProfile profile,
string container,
string audioCodec,
int? audioBitrate,
@ -118,7 +118,7 @@ public static class ContentFeatureBuilder
}
public static IEnumerable<string> BuildVideoHeader(
DeviceProfile profile,
DlnaDeviceProfile profile,
string container,
string videoCodec,
string audioCodec,

View File

@ -0,0 +1,60 @@
using System;
namespace Jellyfin.Plugin.Dlna.Model;
public class DeviceIdentification
{
/// <summary>
/// Gets or sets the name of the friendly.
/// </summary>
/// <value>The name of the friendly.</value>
public string FriendlyName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the model number.
/// </summary>
/// <value>The model number.</value>
public string ModelNumber { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the serial number.
/// </summary>
/// <value>The serial number.</value>
public string SerialNumber { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the name of the model.
/// </summary>
/// <value>The name of the model.</value>
public string ModelName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the model description.
/// </summary>
/// <value>The model description.</value>
public string ModelDescription { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the model URL.
/// </summary>
/// <value>The model URL.</value>
public string ModelUrl { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the manufacturer.
/// </summary>
/// <value>The manufacturer.</value>
public string Manufacturer { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the manufacturer URL.
/// </summary>
/// <value>The manufacturer URL.</value>
public string ManufacturerUrl { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the headers.
/// </summary>
/// <value>The headers.</value>
public HttpHeaderInfo[] Headers { get; set; } = Array.Empty<HttpHeaderInfo>();
}

View File

@ -0,0 +1,24 @@
#nullable disable
namespace Jellyfin.Plugin.Dlna.Model;
public class DeviceProfileInfo
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public DeviceProfileType Type { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Jellyfin.Plugin.Dlna.Model;
public enum DeviceProfileType
{
System = 0,
User = 1
}

View File

@ -0,0 +1,403 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.MediaInfo;
namespace Jellyfin.Plugin.Dlna.Model;
[XmlRoot("Profile")]
public class DlnaDeviceProfile : DeviceProfile
{
/// <summary>
/// Gets or sets the Identification.
/// </summary>
public DeviceIdentification? Identification { get; set; }
/// <summary>
/// Gets or sets the friendly name of the device profile, which can be shown to users.
/// </summary>
public string? FriendlyName { get; set; }
/// <summary>
/// Gets or sets the manufacturer of the device which this profile represents.
/// </summary>
public string? Manufacturer { get; set; }
/// <summary>
/// Gets or sets an url for the manufacturer of the device which this profile represents.
/// </summary>
public string? ManufacturerUrl { get; set; }
/// <summary>
/// Gets or sets the model name of the device which this profile represents.
/// </summary>
public string? ModelName { get; set; }
/// <summary>
/// Gets or sets the model description of the device which this profile represents.
/// </summary>
public string? ModelDescription { get; set; }
/// <summary>
/// Gets or sets the model number of the device which this profile represents.
/// </summary>
public string? ModelNumber { get; set; }
/// <summary>
/// Gets or sets the ModelUrl.
/// </summary>
public string? ModelUrl { get; set; }
/// <summary>
/// Gets or sets the serial number of the device which this profile represents.
/// </summary>
public string? SerialNumber { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableAlbumArtInDidl.
/// </summary>
[DefaultValue(false)]
public bool EnableAlbumArtInDidl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableSingleAlbumArtLimit.
/// </summary>
[DefaultValue(false)]
public bool EnableSingleAlbumArtLimit { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableSingleSubtitleLimit.
/// </summary>
[DefaultValue(false)]
public bool EnableSingleSubtitleLimit { get; set; }
/// <summary>
/// Gets or sets the SupportedMediaTypes.
/// </summary>
public string SupportedMediaTypes { get; set; } = "Audio,Photo,Video";
/// <summary>
/// Gets or sets the UserId.
/// </summary>
public string? UserId { get; set; }
/// <summary>
/// Gets or sets the AlbumArtPn.
/// </summary>
public string? AlbumArtPn { get; set; }
/// <summary>
/// Gets or sets the MaxAlbumArtWidth.
/// </summary>
public int? MaxAlbumArtWidth { get; set; }
/// <summary>
/// Gets or sets the MaxAlbumArtHeight.
/// </summary>
public int? MaxAlbumArtHeight { get; set; }
/// <summary>
/// Gets or sets the maximum allowed width of embedded icons.
/// </summary>
public int? MaxIconWidth { get; set; }
/// <summary>
/// Gets or sets the maximum allowed height of embedded icons.
/// </summary>
public int? MaxIconHeight { get; set; }
/// <summary>
/// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.
/// </summary>
public string? SonyAggregationFlags { get; set; }
/// <summary>
/// Gets or sets the ProtocolInfo.
/// </summary>
public string? ProtocolInfo { get; set; }
/// <summary>
/// Gets or sets the TimelineOffsetSeconds.
/// </summary>
[DefaultValue(0)]
public int TimelineOffsetSeconds { get; set; }
/// <summary>
/// Gets or sets a value indicating whether RequiresPlainVideoItems.
/// </summary>
[DefaultValue(false)]
public bool RequiresPlainVideoItems { get; set; }
/// <summary>
/// Gets or sets a value indicating whether RequiresPlainFolders.
/// </summary>
[DefaultValue(false)]
public bool RequiresPlainFolders { get; set; }
/// <summary>
/// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar.
/// </summary>
[DefaultValue(false)]
public bool EnableMSMediaReceiverRegistrar { get; set; }
/// <summary>
/// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests.
/// </summary>
[DefaultValue(false)]
public bool IgnoreTranscodeByteRangeRequests { get; set; }
/// <summary>
/// Gets or sets the XmlRootAttributes.
/// </summary>
public XmlAttribute[] XmlRootAttributes { get; set; } = Array.Empty<XmlAttribute>();
/// <summary>
/// Gets or sets the ResponseProfiles.
/// </summary>
public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty<ResponseProfile>();
/// <summary>
/// The GetSupportedMediaTypes.
/// </summary>
/// <returns>The .</returns>
public MediaType[] GetSupportedMediaTypes()
{
return ContainerProfile.SplitValue(SupportedMediaTypes)
.Select(m => Enum.TryParse<MediaType>(m, out var parsed) ? parsed : MediaType.Unknown)
.Where(m => m != MediaType.Unknown)
.ToArray();
}
/// <summary>
/// Gets the audio transcoding profile.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="audioCodec">The audio Codec.</param>
/// <returns>A <see cref="TranscodingProfile"/>.</returns>
public TranscodingProfile? GetAudioTranscodingProfile(string? container, string? audioCodec)
{
container = (container ?? string.Empty).TrimStart('.');
return TranscodingProfiles
.Where(i => i.Type == DlnaProfileType.Audio)
.Where(i => string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(i => i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Gets the video transcoding profile.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="audioCodec">The audio Codec.</param>
/// <param name="videoCodec">The video Codec.</param>
/// <returns>The <see cref="TranscodingProfile"/>.</returns>
public TranscodingProfile? GetVideoTranscodingProfile(string? container, string? audioCodec, string? videoCodec)
{
container = (container ?? string.Empty).TrimStart('.');
return TranscodingProfiles
.Where(i => i.Type == DlnaProfileType.Video)
.Where(i => string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase))
.Where(i => i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(i => string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Gets the audio media profile.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="audioCodec">The audio codec.</param>
/// <param name="audioChannels">The audio channels.</param>
/// <param name="audioBitrate">The audio bitrate.</param>
/// <param name="audioSampleRate">The audio sample rate.</param>
/// <param name="audioBitDepth">The audio bit depth.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
public ResponseProfile? GetAudioMediaProfile(string? container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
{
foreach (var i in ResponseProfiles)
{
if (i.Type != DlnaProfileType.Audio)
{
continue;
}
if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
{
continue;
}
var audioCodecs = i.GetAudioCodecs();
if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
continue;
}
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
if (!ConditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate, audioSampleRate, audioBitDepth))
{
anyOff = true;
break;
}
}
if (anyOff)
{
continue;
}
return i;
}
return null;
}
/// <summary>
/// Gets the image media profile.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
public ResponseProfile? GetImageMediaProfile(string container, int? width, int? height)
{
foreach (var i in ResponseProfiles)
{
if (i.Type != DlnaProfileType.Photo)
{
continue;
}
if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
{
continue;
}
var anyOff = false;
foreach (var c in i.Conditions)
{
if (!ConditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height))
{
anyOff = true;
break;
}
}
if (anyOff)
{
continue;
}
return i;
}
return null;
}
/// <summary>
/// Gets the video media profile.
/// </summary>
/// <param name="container">The container.</param>
/// <param name="audioCodec">The audio codec.</param>
/// <param name="videoCodec">The video codec.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="bitDepth">The bit depth.</param>
/// <param name="videoBitrate">The video bitrate.</param>
/// <param name="videoProfile">The video profile.</param>
/// <param name="videoRangeType">The video range type.</param>
/// <param name="videoLevel">The video level.</param>
/// <param name="videoFramerate">The video framerate.</param>
/// <param name="packetLength">The packet length.</param>
/// <param name="timestamp">The timestamp<see cref="TransportStreamTimestamp"/>.</param>
/// <param name="isAnamorphic">True if anamorphic.</param>
/// <param name="isInterlaced">True if interlaced.</param>
/// <param name="refFrames">The ref frames.</param>
/// <param name="numVideoStreams">The number of video streams.</param>
/// <param name="numAudioStreams">The number of audio streams.</param>
/// <param name="videoCodecTag">The video Codec tag.</param>
/// <param name="isAvc">True if Avc.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
public ResponseProfile? GetVideoMediaProfile(
string? container,
string? audioCodec,
string? videoCodec,
int? width,
int? height,
int? bitDepth,
int? videoBitrate,
string? videoProfile,
VideoRangeType videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
TransportStreamTimestamp timestamp,
bool? isAnamorphic,
bool? isInterlaced,
int? refFrames,
int? numVideoStreams,
int? numAudioStreams,
string? videoCodecTag,
bool? isAvc)
{
foreach (var i in ResponseProfiles)
{
if (i.Type != DlnaProfileType.Video)
{
continue;
}
if (!ContainerProfile.ContainsContainer(i.GetContainers(), container))
{
continue;
}
var audioCodecs = i.GetAudioCodecs();
if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
continue;
}
var videoCodecs = i.GetVideoCodecs();
if (videoCodecs.Length > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
continue;
}
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
{
anyOff = true;
break;
}
}
if (anyOff)
{
continue;
}
return i;
}
return null;
}
private static ProfileCondition GetModelProfileCondition(ProfileCondition c)
{
return new ProfileCondition
{
Condition = c.Condition,
IsRequired = c.IsRequired,
Property = c.Property,
Value = c.Value
};
}
}

View File

@ -0,0 +1,8 @@
namespace Jellyfin.Plugin.Dlna.Model;
public enum HeaderMatchType
{
Equals = 0,
Regex = 1,
Substring = 2
}

View File

@ -0,0 +1,17 @@
#nullable disable
using System.Xml.Serialization;
namespace Jellyfin.Plugin.Dlna.Model;
public class HttpHeaderInfo
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
[XmlAttribute("match")]
public HeaderMatchType Match { get; set; }
}

View File

@ -20,26 +20,26 @@ public interface IDlnaManager
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>DeviceProfile.</returns>
DeviceProfile? GetProfile(IHeaderDictionary headers);
DlnaDeviceProfile? GetProfile(IHeaderDictionary headers);
/// <summary>
/// Gets the default profile.
/// </summary>
/// <returns>DeviceProfile.</returns>
DeviceProfile GetDefaultProfile();
DlnaDeviceProfile GetDefaultProfile();
/// <summary>
/// Creates the profile.
/// </summary>
/// <param name="profile">The profile.</param>
void CreateProfile(DeviceProfile profile);
void CreateProfile(DlnaDeviceProfile profile);
/// <summary>
/// Updates the profile.
/// </summary>
/// <param name="profileId">The profile id.</param>
/// <param name="profile">The profile.</param>
void UpdateProfile(string profileId, DeviceProfile profile);
void UpdateProfile(string profileId, DlnaDeviceProfile profile);
/// <summary>
/// Deletes the profile.
@ -52,14 +52,14 @@ public interface IDlnaManager
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>DeviceProfile.</returns>
DeviceProfile? GetProfile(string id);
DlnaDeviceProfile? GetProfile(string id);
/// <summary>
/// Gets the profile.
/// </summary>
/// <param name="deviceInfo">The device information.</param>
/// <returns>DeviceProfile.</returns>
DeviceProfile? GetProfile(DeviceIdentification deviceInfo);
DlnaDeviceProfile? GetProfile(DeviceIdentification deviceInfo);
/// <summary>
/// Gets the server description XML.

View File

@ -5,7 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Model" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20240112.4" />
<PackageReference Include="Jellyfin.Extensions" Version="10.9.0-20240112.4" />
<PackageReference Include="Jellyfin.Model" Version="10.9.0-20240112.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,50 @@
#nullable disable
using System;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna;
namespace Jellyfin.Plugin.Dlna.Model;
public class ResponseProfile
{
public ResponseProfile()
{
Conditions = Array.Empty<ProfileCondition>();
}
[XmlAttribute("container")]
public string Container { get; set; }
[XmlAttribute("audioCodec")]
public string AudioCodec { get; set; }
[XmlAttribute("videoCodec")]
public string VideoCodec { get; set; }
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
[XmlAttribute("orgPn")]
public string OrgPn { get; set; }
[XmlAttribute("mimeType")]
public string MimeType { get; set; }
public ProfileCondition[] Conditions { get; set; }
public string[] GetContainers()
{
return ContainerProfile.SplitValue(Container);
}
public string[] GetAudioCodecs()
{
return ContainerProfile.SplitValue(AudioCodec);
}
public string[] GetVideoCodecs()
{
return ContainerProfile.SplitValue(VideoCodec);
}
}

View File

@ -0,0 +1,23 @@
#nullable disable
using System.Xml.Serialization;
namespace Jellyfin.Plugin.Dlna.Model;
/// <summary>
/// Defines the <see cref="XmlAttribute" />.
/// </summary>
public class XmlAttribute
{
/// <summary>
/// Gets or sets the name of the attribute.
/// </summary>
[XmlAttribute("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the value of the attribute.
/// </summary>
[XmlAttribute("value")]
public string Value { get; set; }
}

View File

@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Model" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20240112.4" />
<PackageReference Include="Jellyfin.Model" Version="10.9.0-20240112.4" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.0" />
</ItemGroup>

View File

@ -1,4 +1,5 @@
using System;
using Jellyfin.Plugin.Dlna.Model;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
@ -144,7 +145,7 @@ public class StreamState : EncodingJobInfo, IDisposable
/// <summary>
/// Gets or sets the device profile.
/// </summary>
public DeviceProfile? DeviceProfile { get; set; }
public DlnaDeviceProfile? DeviceProfile { get; set; }
/// <summary>
/// Gets or sets the transcoding job.

View File

@ -528,7 +528,7 @@ public static class StreamingHelpers
if (state.DeviceProfile is null)
{
var caps = deviceManager.GetCapabilities(deviceProfileId);
state.DeviceProfile = caps is null ? dlnaManager.GetProfile(request.Headers) : caps.DeviceProfile;
state.DeviceProfile = caps is null ? dlnaManager.GetProfile(request.Headers) : caps.DeviceProfile as DlnaDeviceProfile;
}
}

View File

@ -102,7 +102,7 @@ public class DlnaController : ControllerBase
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Profiles")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CreateProfile([FromBody] DeviceProfile deviceProfile)
public ActionResult CreateProfile([FromBody] DlnaDeviceProfile deviceProfile)
{
_dlnaManager.CreateProfile(deviceProfile);
return NoContent();
@ -119,7 +119,7 @@ public class DlnaController : ControllerBase
[HttpPost("Profiles/{profileId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateProfile([FromRoute, Required] string profileId, [FromBody] DeviceProfile deviceProfile)
public ActionResult UpdateProfile([FromRoute, Required] string profileId, [FromBody] DlnaDeviceProfile deviceProfile)
{
var existingDeviceProfile = _dlnaManager.GetProfile(profileId);
if (existingDeviceProfile is null)

View File

@ -3,9 +3,9 @@
using System;
using System.Collections.Generic;
using System.Xml;
using Jellyfin.Plugin.Dlna.Model;
using Jellyfin.Plugin.Dlna.Service;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Dlna;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.Dlna.ConnectionManager;
@ -15,14 +15,14 @@ namespace Jellyfin.Plugin.Dlna.ConnectionManager;
/// </summary>
public class ControlHandler : BaseControlHandler
{
private readonly DeviceProfile _profile;
private readonly DlnaDeviceProfile _profile;
/// <summary>
/// Initializes a new instance of the <see cref="ControlHandler"/> class.
/// </summary>
/// <param name="logger">The <see cref="ILogger"/> for use with the <see cref="ControlHandler"/> instance.</param>
/// <param name="profile">The <see cref="DeviceProfile"/> for use with the <see cref="ControlHandler"/> instance.</param>
public ControlHandler(ILogger logger, DeviceProfile profile)
/// <param name="profile">The <see cref="DlnaDeviceProfile"/> for use with the <see cref="ControlHandler"/> instance.</param>
public ControlHandler(ILogger logger, DlnaDeviceProfile profile)
: base(logger)
{
_profile = profile;

View File

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Plugin.Dlna.Model;
using Jellyfin.Plugin.Dlna.Service;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@ -129,7 +130,7 @@ public class ContentDirectoryService : BaseService, IContentDirectory
/// </summary>
/// <param name="profile">The <see cref="DeviceProfile"/>.</param>
/// <returns>The <see cref="User"/>.</returns>
private User? GetUser(DeviceProfile profile)
private User? GetUser(DlnaDeviceProfile profile)
{
if (!string.IsNullOrEmpty(profile.UserId))
{

View File

@ -51,7 +51,7 @@ public class ControlHandler : BaseControlHandler
private readonly DidlBuilder _didlBuilder;
private readonly DeviceProfile _profile;
private readonly DlnaDeviceProfile _profile;
/// <summary>
/// Initializes a new instance of the <see cref="ControlHandler"/> class.
@ -73,7 +73,7 @@ public class ControlHandler : BaseControlHandler
public ControlHandler(
ILogger logger,
ILibraryManager libraryManager,
DeviceProfile profile,
DlnaDeviceProfile profile,
string serverAddress,
string accessToken,
IImageProcessor imageProcessor,

View File

@ -36,7 +36,7 @@ using StreamBuilder = MediaBrowser.Model.Dlna.StreamBuilder;
using StreamInfo = MediaBrowser.Model.Dlna.StreamInfo;
using SubtitleDeliveryMethod = MediaBrowser.Model.Dlna.SubtitleDeliveryMethod;
using SubtitleStreamInfo = MediaBrowser.Model.Dlna.SubtitleStreamInfo;
using XmlAttribute = MediaBrowser.Model.Dlna.XmlAttribute;
using XmlAttribute = Jellyfin.Plugin.Dlna.Model.XmlAttribute;
namespace Jellyfin.Plugin.Dlna.Didl;
@ -47,7 +47,7 @@ public class DidlBuilder
private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/";
private readonly DeviceProfile _profile;
private readonly DlnaDeviceProfile _profile;
private readonly IImageProcessor _imageProcessor;
private readonly string _serverAddress;
private readonly string? _accessToken;
@ -60,7 +60,7 @@ public class DidlBuilder
private readonly ILibraryManager _libraryManager;
public DidlBuilder(
DeviceProfile profile,
DlnaDeviceProfile profile,
User? user,
IImageProcessor imageProcessor,
string serverAddress,
@ -126,7 +126,7 @@ public class DidlBuilder
}
}
public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer)
public static void WriteXmlRootAttributes(DlnaDeviceProfile profile, XmlWriter writer)
{
foreach (var att in profile.XmlRootAttributes)
{

View File

@ -10,6 +10,7 @@ using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Jellyfin.Extensions.Json;
using Jellyfin.Plugin.Dlna.Model;
using Jellyfin.Plugin.Dlna.Profiles;
using Jellyfin.Plugin.Dlna.Server;
using MediaBrowser.Common.Configuration;
@ -37,7 +38,8 @@ public class DlnaManager : IDlnaManager
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
private readonly Dictionary<string, Tuple<InternalProfileInfo, DlnaDeviceProfile>> _profiles
= new(StringComparer.Ordinal);
public DlnaManager(
IXmlSerializer xmlSerializer,
@ -81,7 +83,7 @@ public class DlnaManager : IDlnaManager
.OrderBy(i => i.Name));
}
public IEnumerable<DeviceProfile> GetProfiles()
public IEnumerable<DlnaDeviceProfile> GetProfiles()
{
lock (_profiles)
{
@ -94,13 +96,13 @@ public class DlnaManager : IDlnaManager
}
/// <inheritdoc />
public DeviceProfile GetDefaultProfile()
public DlnaDeviceProfile GetDefaultProfile()
{
return new DefaultProfile();
}
/// <inheritdoc />
public DeviceProfile? GetProfile(DeviceIdentification deviceInfo)
public DlnaDeviceProfile? GetProfile(DeviceIdentification deviceInfo)
{
ArgumentNullException.ThrowIfNull(deviceInfo);
@ -167,7 +169,7 @@ public class DlnaManager : IDlnaManager
}
/// <inheritdoc />
public DeviceProfile? GetProfile(IHeaderDictionary headers)
public DlnaDeviceProfile? GetProfile(IHeaderDictionary headers)
{
ArgumentNullException.ThrowIfNull(headers);
@ -239,23 +241,23 @@ public class DlnaManager : IDlnaManager
}
}
private DeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
private DlnaDeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
{
lock (_profiles)
{
if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DeviceProfile>? profileTuple))
if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DlnaDeviceProfile>? profileTuple))
{
return profileTuple.Item2;
}
try
{
var tempProfile = (DeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DeviceProfile), path);
var tempProfile = (DlnaDeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DlnaDeviceProfile), path);
var profile = ReserializeProfile(tempProfile);
profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
_profiles[path] = new Tuple<InternalProfileInfo, DlnaDeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
return profile;
}
@ -269,7 +271,7 @@ public class DlnaManager : IDlnaManager
}
/// <inheritdoc />
public DeviceProfile? GetProfile(string id)
public DlnaDeviceProfile? GetProfile(string id)
{
ArgumentException.ThrowIfNullOrEmpty(id);
@ -370,7 +372,7 @@ public class DlnaManager : IDlnaManager
}
/// <inheritdoc />
public void CreateProfile(DeviceProfile profile)
public void CreateProfile(DlnaDeviceProfile profile)
{
profile = ReserializeProfile(profile);
@ -383,7 +385,7 @@ public class DlnaManager : IDlnaManager
}
/// <inheritdoc />
public void UpdateProfile(string profileId, DeviceProfile profile)
public void UpdateProfile(string profileId, DlnaDeviceProfile profile)
{
profile = ReserializeProfile(profile);
@ -411,11 +413,11 @@ public class DlnaManager : IDlnaManager
SaveProfile(profile, path, DeviceProfileType.User);
}
private void SaveProfile(DeviceProfile profile, string path, DeviceProfileType type)
private void SaveProfile(DlnaDeviceProfile profile, string path, DeviceProfileType type)
{
lock (_profiles)
{
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
_profiles[path] = new Tuple<InternalProfileInfo, DlnaDeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
}
SerializeToXml(profile, path);
@ -432,9 +434,9 @@ public class DlnaManager : IDlnaManager
/// </summary>
/// <param name="profile">The device profile.</param>
/// <returns>The re-serialized device profile.</returns>
private DeviceProfile ReserializeProfile(DeviceProfile profile)
private DlnaDeviceProfile ReserializeProfile(DlnaDeviceProfile profile)
{
if (profile.GetType() == typeof(DeviceProfile))
if (profile.GetType() == typeof(DlnaDeviceProfile))
{
return profile;
}
@ -442,7 +444,7 @@ public class DlnaManager : IDlnaManager
var json = JsonSerializer.Serialize(profile, _jsonOptions);
// Output can't be null if the input isn't null
return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions)!;
return JsonSerializer.Deserialize<DlnaDeviceProfile>(json, _jsonOptions)!;
}
/// <inheritdoc />

View File

@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Model" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20240112.4" />
<PackageReference Include="Jellyfin.Model" Version="10.9.0-20240112.4" />
</ItemGroup>
<ItemGroup>

View File

@ -4,7 +4,7 @@
using System.Collections.Generic;
using Jellyfin.Plugin.Dlna.Common;
using MediaBrowser.Model.Dlna;
using Jellyfin.Plugin.Dlna.Model;
namespace Jellyfin.Plugin.Dlna.PlayTo;

View File

@ -11,6 +11,7 @@ using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using Jellyfin.Plugin.Dlna.Didl;
using Jellyfin.Plugin.Dlna.Extensions;
using Jellyfin.Plugin.Dlna.Model;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@ -579,7 +580,7 @@ public class PlayToController2 : ISessionController, IDisposable
return null;
}
private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DeviceProfile profile, string deviceId, string? mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DlnaDeviceProfile profile, string deviceId, string? mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
=> item.MediaType switch
{
MediaType.Video => new PlaylistItem

View File

@ -2,6 +2,7 @@
#pragma warning disable CS1591
using Jellyfin.Plugin.Dlna.Model;
using MediaBrowser.Model.Dlna;
namespace Jellyfin.Plugin.Dlna.PlayTo;
@ -14,5 +15,5 @@ public class PlaylistItem
public StreamInfo StreamInfo { get; set; }
public DeviceProfile Profile { get; set; }
public DlnaDeviceProfile Profile { get; set; }
}

View File

@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
using Jellyfin.Plugin.Dlna.Model;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Session;
@ -12,7 +13,7 @@ namespace Jellyfin.Plugin.Dlna.PlayTo;
public static class PlaylistItemFactory
{
public static PlaylistItem Create(Photo item, DeviceProfile profile)
public static PlaylistItem Create(Photo item, DlnaDeviceProfile profile)
{
var playlistItem = new PlaylistItem
{

View File

@ -2,12 +2,13 @@
using System;
using System.Globalization;
using Jellyfin.Plugin.Dlna.Model;
using MediaBrowser.Model.Dlna;
namespace Jellyfin.Plugin.Dlna.Profiles;
[System.Xml.Serialization.XmlRoot("Profile")]
public class DefaultProfile : DeviceProfile
public class DefaultProfile : DlnaDeviceProfile
{
public DefaultProfile()
{

View File

@ -7,20 +7,20 @@ using System.Linq;
using System.Security;
using System.Text;
using Jellyfin.Plugin.Dlna.Common;
using MediaBrowser.Model.Dlna;
using Jellyfin.Plugin.Dlna.Model;
namespace Jellyfin.Plugin.Dlna.Server;
public class DescriptionXmlBuilder
{
private readonly DeviceProfile _profile;
private readonly DlnaDeviceProfile _profile;
private readonly string _serverUdn;
private readonly string _serverAddress;
private readonly string _serverName;
private readonly string _serverId;
public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId)
public DescriptionXmlBuilder(DlnaDeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId)
{
ArgumentException.ThrowIfNullOrEmpty(serverUdn);
ArgumentException.ThrowIfNullOrEmpty(serverAddress);

View File

@ -5,9 +5,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jellyfin.Common" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Extensions" Version="10.9.0-20231229.2" />
<PackageReference Include="Jellyfin.Common" Version="10.9.0-20240112.4" />
<PackageReference Include="Jellyfin.Controller" Version="10.9.0-20240112.4" />
<PackageReference Include="Jellyfin.Extensions" Version="10.9.0-20240112.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup>
</Project>