diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs new file mode 100644 index 0000000000..4c587391fc --- /dev/null +++ b/Jellyfin.Api/Extensions/DtoExtensions.cs @@ -0,0 +1,162 @@ +using System; +using System.Linq; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using Microsoft.AspNetCore.Http; + +namespace Jellyfin.Api.Extensions +{ + /// + /// Dto Extensions. + /// + public static class DtoExtensions + { + /// + /// Add Dto Item fields. + /// + /// + /// Converted from IHasItemFields. + /// Legacy order: 1. + /// + /// DtoOptions object. + /// Comma delimited string of fields. + /// Modified DtoOptions object. + internal static DtoOptions AddItemFields(this DtoOptions dtoOptions, string fields) + { + if (string.IsNullOrEmpty(fields)) + { + dtoOptions.Fields = Array.Empty(); + } + else + { + dtoOptions.Fields = fields.Split(',') + .Select(v => + { + if (Enum.TryParse(v, true, out ItemFields value)) + { + return (ItemFields?)value; + } + + return null; + }) + .Where(i => i.HasValue) + .Select(i => i!.Value) + .ToArray(); + } + + return dtoOptions; + } + + /// + /// Add additional fields depending on client. + /// + /// + /// Use in place of GetDtoOptions. + /// Legacy order: 2. + /// + /// DtoOptions object. + /// Current request. + /// Modified DtoOptions object. + internal static DtoOptions AddClientFields( + this DtoOptions dtoOptions, HttpRequest request) + { + dtoOptions.Fields ??= Array.Empty(); + + string? client = ClaimHelpers.GetClient(request.HttpContext.User); + + // No client in claim + if (string.IsNullOrEmpty(client)) + { + return dtoOptions; + } + + if (!dtoOptions.ContainsField(ItemFields.RecursiveItemCount)) + { + if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1) + { + int oldLen = dtoOptions.Fields.Length; + var arr = new ItemFields[oldLen + 1]; + dtoOptions.Fields.CopyTo(arr, 0); + arr[oldLen] = ItemFields.RecursiveItemCount; + dtoOptions.Fields = arr; + } + } + + if (!dtoOptions.ContainsField(ItemFields.ChildCount)) + { + if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1) + { + int oldLen = dtoOptions.Fields.Length; + var arr = new ItemFields[oldLen + 1]; + dtoOptions.Fields.CopyTo(arr, 0); + arr[oldLen] = ItemFields.ChildCount; + dtoOptions.Fields = arr; + } + } + + return dtoOptions; + } + + /// + /// Add additional DtoOptions. + /// + /// + /// Converted from IHasDtoOptions. + /// Legacy order: 3. + /// + /// DtoOptions object. + /// Enable images. + /// Enable user data. + /// Image type limit. + /// Enable image types. + /// Modified DtoOptions object. + internal static DtoOptions AddAdditionalDtoOptions( + in DtoOptions dtoOptions, + bool? enableImages, + bool? enableUserData, + int? imageTypeLimit, + string enableImageTypes) + { + dtoOptions.EnableImages = enableImages ?? true; + + if (imageTypeLimit.HasValue) + { + dtoOptions.ImageTypeLimit = imageTypeLimit.Value; + } + + if (enableUserData.HasValue) + { + dtoOptions.EnableUserData = enableUserData.Value; + } + + if (!string.IsNullOrWhiteSpace(enableImageTypes)) + { + dtoOptions.ImageTypes = enableImageTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)) + .ToArray(); + } + + return dtoOptions; + } + + /// + /// Check if DtoOptions contains field. + /// + /// DtoOptions object. + /// Field to check. + /// Field existence. + internal static bool ContainsField(this DtoOptions dtoOptions, ItemFields field) + => dtoOptions.Fields != null && dtoOptions.Fields.Contains(field); + } +}