From eee02a355af0760c55dc9c1d42b9f5f623135b08 Mon Sep 17 00:00:00 2001 From: ZadenRB Date: Wed, 22 Apr 2020 10:06:37 -0600 Subject: Adds produces annotation to the base controller to indicate application/json as the response type for endpoints --- Jellyfin.Api/BaseJellyfinApiController.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Jellyfin.Api/BaseJellyfinApiController.cs') diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 1f4508e6c..51bd384b3 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -7,6 +7,7 @@ namespace Jellyfin.Api /// [ApiController] [Route("[controller]")] + [Produces("application/json")] public class BaseJellyfinApiController : ControllerBase { } -- cgit v1.2.3 From 2066b0f68f96a14d7b5a5e511ce9099c7a1cada7 Mon Sep 17 00:00:00 2001 From: ZadenRB Date: Thu, 23 Apr 2020 16:15:59 -0600 Subject: Use builtin JSON Mime type constant --- Jellyfin.Api/BaseJellyfinApiController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Api/BaseJellyfinApiController.cs') diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 51bd384b3..a34f9eb62 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -1,3 +1,4 @@ +using System.Net.Mime; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api @@ -7,7 +8,7 @@ namespace Jellyfin.Api /// [ApiController] [Route("[controller]")] - [Produces("application/json")] + [Produces(MediaTypeNames.Application.Json)] public class BaseJellyfinApiController : ControllerBase { } -- cgit v1.2.3 From 638cfa32abc3ffd61119f774ffc5a0f5a490d3e9 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 2 Jun 2020 15:07:07 +0200 Subject: Move SearchService to new API endpoint --- Jellyfin.Api/BaseJellyfinApiController.cs | 19 ++ Jellyfin.Api/Controllers/SearchController.cs | 266 +++++++++++++++++++++ MediaBrowser.Api/SearchService.cs | 333 --------------------------- 3 files changed, 285 insertions(+), 333 deletions(-) create mode 100644 Jellyfin.Api/Controllers/SearchController.cs delete mode 100644 MediaBrowser.Api/SearchService.cs (limited to 'Jellyfin.Api/BaseJellyfinApiController.cs') diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 1f4508e6c..6a9e48f8d 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api @@ -9,5 +10,23 @@ namespace Jellyfin.Api [Route("[controller]")] public class BaseJellyfinApiController : ControllerBase { + /// + /// Splits a string at a seperating character into an array of substrings. + /// + /// The string to split. + /// The char that seperates the substrings. + /// Option to remove empty substrings from the array. + /// An array of the substrings. + internal static string[] Split(string value, char separator, bool removeEmpty) + { + if (string.IsNullOrWhiteSpace(value)) + { + return Array.Empty(); + } + + return removeEmpty + ? value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) + : value.Split(separator); + } } } diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs new file mode 100644 index 000000000..15a650bf9 --- /dev/null +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -0,0 +1,266 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Linq; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Search; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers +{ + /// + /// Search controller. + /// + [Route("/Search/Hints")] + [Authenticated] + public class SearchController : BaseJellyfinApiController + { + private readonly ISearchEngine _searchEngine; + private readonly ILibraryManager _libraryManager; + private readonly IDtoService _dtoService; + private readonly IImageProcessor _imageProcessor; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of interface. + /// Instance of interface. + /// Instance of interface. + /// Instance of interface. + public SearchController( + ISearchEngine searchEngine, + ILibraryManager libraryManager, + IDtoService dtoService, + IImageProcessor imageProcessor) + { + _searchEngine = searchEngine; + _libraryManager = libraryManager; + _dtoService = dtoService; + _imageProcessor = imageProcessor; + } + + /// + /// Gets the search hint result. + /// + /// The record index to start at. All items with a lower index will be dropped from the results. + /// The maximum number of records to return. + /// Supply a user id to search within a user's library or omit to search all. + /// The search term to filter on. + /// Optional filter whether to include people. + /// Optional filter whether to include media. + /// Optional filter whether to include genres. + /// Optional filter whether to include studios. + /// Optional filter whether to include artists. + /// If specified, only results with the specified item types are returned. This allows multiple, comma delimeted. + /// If specified, results with these item types are filtered out. This allows multiple, comma delimeted. + /// If specified, only results with the specified media types are returned. This allows multiple, comma delimeted. + /// If specified, only children of the parent are returned. + /// Optional filter for movies. + /// Optional filter for series. + /// Optional filter for news. + /// Optional filter for kids. + /// Optional filter for sports. + /// An with the results of the search. + [HttpGet] + [Description("Gets search hints based on a search term")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult Get( + [FromQuery] int? startIndex, + [FromQuery] int? limit, + [FromQuery] Guid userId, + [FromQuery, Required] string searchTerm, + [FromQuery] bool includePeople, + [FromQuery] bool includeMedia, + [FromQuery] bool includeGenres, + [FromQuery] bool includeStudios, + [FromQuery] bool includeArtists, + [FromQuery] string includeItemTypes, + [FromQuery] string excludeItemTypes, + [FromQuery] string mediaTypes, + [FromQuery] string parentId, + [FromQuery] bool? isMovie, + [FromQuery] bool? isSeries, + [FromQuery] bool? isNews, + [FromQuery] bool? isKids, + [FromQuery] bool? isSports) + { + var result = _searchEngine.GetSearchHints(new SearchQuery + { + Limit = limit, + SearchTerm = searchTerm, + IncludeArtists = includeArtists, + IncludeGenres = includeGenres, + IncludeMedia = includeMedia, + IncludePeople = includePeople, + IncludeStudios = includeStudios, + StartIndex = startIndex, + UserId = userId, + IncludeItemTypes = Split(includeItemTypes, ',', true), + ExcludeItemTypes = Split(excludeItemTypes, ',', true), + MediaTypes = Split(mediaTypes, ',', true), + ParentId = parentId, + + IsKids = isKids, + IsMovie = isMovie, + IsNews = isNews, + IsSeries = isSeries, + IsSports = isSports + }); + + return Ok(new SearchHintResult + { + TotalRecordCount = result.TotalRecordCount, + SearchHints = result.Items.Select(GetSearchHintResult).ToArray() + }); + } + + /// + /// Gets the search hint result. + /// + /// The hint info. + /// SearchHintResult. + private SearchHint GetSearchHintResult(SearchHintInfo hintInfo) + { + var item = hintInfo.Item; + + var result = new SearchHint + { + Name = item.Name, + IndexNumber = item.IndexNumber, + ParentIndexNumber = item.ParentIndexNumber, + Id = item.Id, + Type = item.GetClientTypeName(), + MediaType = item.MediaType, + MatchedTerm = hintInfo.MatchedTerm, + RunTimeTicks = item.RunTimeTicks, + ProductionYear = item.ProductionYear, + ChannelId = item.ChannelId, + EndDate = item.EndDate + }; + + // legacy + result.ItemId = result.Id; + + if (item.IsFolder) + { + result.IsFolder = true; + } + + var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary); + + if (primaryImageTag != null) + { + result.PrimaryImageTag = primaryImageTag; + result.PrimaryImageAspectRatio = _dtoService.GetPrimaryImageAspectRatio(item); + } + + SetThumbImageInfo(result, item); + SetBackdropImageInfo(result, item); + + switch (item) + { + case IHasSeries hasSeries: + result.Series = hasSeries.SeriesName; + break; + case LiveTvProgram program: + result.StartDate = program.StartDate; + break; + case Series series: + if (series.Status.HasValue) + { + result.Status = series.Status.Value.ToString(); + } + + break; + case MusicAlbum album: + result.Artists = album.Artists; + result.AlbumArtist = album.AlbumArtist; + break; + case Audio song: + result.AlbumArtist = song.AlbumArtists?[0]; + result.Artists = song.Artists; + + MusicAlbum musicAlbum = song.AlbumEntity; + + if (musicAlbum != null) + { + result.Album = musicAlbum.Name; + result.AlbumId = musicAlbum.Id; + } + else + { + result.Album = song.Album; + } + + break; + } + + if (!item.ChannelId.Equals(Guid.Empty)) + { + var channel = _libraryManager.GetItemById(item.ChannelId); + result.ChannelName = channel?.Name; + } + + return result; + } + + private void SetThumbImageInfo(SearchHint hint, BaseItem item) + { + var itemWithImage = item.HasImage(ImageType.Thumb) ? item : null; + + if (itemWithImage == null && item is Episode) + { + itemWithImage = GetParentWithImage(item, ImageType.Thumb); + } + + if (itemWithImage == null) + { + itemWithImage = GetParentWithImage(item, ImageType.Thumb); + } + + if (itemWithImage != null) + { + var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Thumb); + + if (tag != null) + { + hint.ThumbImageTag = tag; + hint.ThumbImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); + } + } + } + + private void SetBackdropImageInfo(SearchHint hint, BaseItem item) + { + var itemWithImage = (item.HasImage(ImageType.Backdrop) ? item : null) + ?? GetParentWithImage(item, ImageType.Backdrop); + + if (itemWithImage != null) + { + var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Backdrop); + + if (tag != null) + { + hint.BackdropImageTag = tag; + hint.BackdropImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); + } + } + } + + private T GetParentWithImage(BaseItem item, ImageType type) + where T : BaseItem + { + return item.GetParents().OfType().FirstOrDefault(i => i.HasImage(type)); + } + } +} diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs deleted file mode 100644 index e9d339c6e..000000000 --- a/MediaBrowser.Api/SearchService.cs +++ /dev/null @@ -1,333 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Search; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api -{ - /// - /// Class GetSearchHints - /// - [Route("/Search/Hints", "GET", Summary = "Gets search hints based on a search term")] - public class GetSearchHints : IReturn - { - /// - /// Skips over a given number of items within the results. Use for paging. - /// - /// The start index. - [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? StartIndex { get; set; } - - /// - /// The maximum number of items to return - /// - /// The limit. - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "Optional. Supply a user id to search within a user's library or omit to search all.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - - /// - /// Search characters used to find items - /// - /// The index by. - [ApiMember(Name = "SearchTerm", Description = "The search term to filter on", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SearchTerm { get; set; } - - - [ApiMember(Name = "IncludePeople", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool IncludePeople { get; set; } - - [ApiMember(Name = "IncludeMedia", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool IncludeMedia { get; set; } - - [ApiMember(Name = "IncludeGenres", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool IncludeGenres { get; set; } - - [ApiMember(Name = "IncludeStudios", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool IncludeStudios { get; set; } - - [ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool IncludeArtists { get; set; } - - [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string IncludeItemTypes { get; set; } - - [ApiMember(Name = "ExcludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ExcludeItemTypes { get; set; } - - [ApiMember(Name = "MediaTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string MediaTypes { get; set; } - - public string ParentId { get; set; } - - [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsMovie { get; set; } - - [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSeries { get; set; } - - [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsNews { get; set; } - - [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsKids { get; set; } - - [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSports { get; set; } - - public GetSearchHints() - { - IncludeArtists = true; - IncludeGenres = true; - IncludeMedia = true; - IncludePeople = true; - IncludeStudios = true; - } - } - - /// - /// Class SearchService - /// - [Authenticated] - public class SearchService : BaseApiService - { - /// - /// The _search engine - /// - private readonly ISearchEngine _searchEngine; - private readonly ILibraryManager _libraryManager; - private readonly IDtoService _dtoService; - private readonly IImageProcessor _imageProcessor; - - /// - /// Initializes a new instance of the class. - /// - /// The search engine. - /// The library manager. - /// The dto service. - /// The image processor. - public SearchService( - ILogger logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - ISearchEngine searchEngine, - ILibraryManager libraryManager, - IDtoService dtoService, - IImageProcessor imageProcessor) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _searchEngine = searchEngine; - _libraryManager = libraryManager; - _dtoService = dtoService; - _imageProcessor = imageProcessor; - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetSearchHints request) - { - var result = GetSearchHintsAsync(request); - - return ToOptimizedResult(result); - } - - /// - /// Gets the search hints async. - /// - /// The request. - /// Task{IEnumerable{SearchHintResult}}. - private SearchHintResult GetSearchHintsAsync(GetSearchHints request) - { - var result = _searchEngine.GetSearchHints(new SearchQuery - { - Limit = request.Limit, - SearchTerm = request.SearchTerm, - IncludeArtists = request.IncludeArtists, - IncludeGenres = request.IncludeGenres, - IncludeMedia = request.IncludeMedia, - IncludePeople = request.IncludePeople, - IncludeStudios = request.IncludeStudios, - StartIndex = request.StartIndex, - UserId = request.UserId, - IncludeItemTypes = ApiEntryPoint.Split(request.IncludeItemTypes, ',', true), - ExcludeItemTypes = ApiEntryPoint.Split(request.ExcludeItemTypes, ',', true), - MediaTypes = ApiEntryPoint.Split(request.MediaTypes, ',', true), - ParentId = request.ParentId, - - IsKids = request.IsKids, - IsMovie = request.IsMovie, - IsNews = request.IsNews, - IsSeries = request.IsSeries, - IsSports = request.IsSports - - }); - - return new SearchHintResult - { - TotalRecordCount = result.TotalRecordCount, - - SearchHints = result.Items.Select(GetSearchHintResult).ToArray() - }; - } - - /// - /// Gets the search hint result. - /// - /// The hint info. - /// SearchHintResult. - private SearchHint GetSearchHintResult(SearchHintInfo hintInfo) - { - var item = hintInfo.Item; - - var result = new SearchHint - { - Name = item.Name, - IndexNumber = item.IndexNumber, - ParentIndexNumber = item.ParentIndexNumber, - Id = item.Id, - Type = item.GetClientTypeName(), - MediaType = item.MediaType, - MatchedTerm = hintInfo.MatchedTerm, - RunTimeTicks = item.RunTimeTicks, - ProductionYear = item.ProductionYear, - ChannelId = item.ChannelId, - EndDate = item.EndDate - }; - - // legacy - result.ItemId = result.Id; - - if (item.IsFolder) - { - result.IsFolder = true; - } - - var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary); - - if (primaryImageTag != null) - { - result.PrimaryImageTag = primaryImageTag; - result.PrimaryImageAspectRatio = _dtoService.GetPrimaryImageAspectRatio(item); - } - - SetThumbImageInfo(result, item); - SetBackdropImageInfo(result, item); - - switch (item) - { - case IHasSeries hasSeries: - result.Series = hasSeries.SeriesName; - break; - case LiveTvProgram program: - result.StartDate = program.StartDate; - break; - case Series series: - if (series.Status.HasValue) - { - result.Status = series.Status.Value.ToString(); - } - - break; - case MusicAlbum album: - result.Artists = album.Artists; - result.AlbumArtist = album.AlbumArtist; - break; - case Audio song: - result.AlbumArtist = song.AlbumArtists.FirstOrDefault(); - result.Artists = song.Artists; - - MusicAlbum musicAlbum = song.AlbumEntity; - - if (musicAlbum != null) - { - result.Album = musicAlbum.Name; - result.AlbumId = musicAlbum.Id; - } - else - { - result.Album = song.Album; - } - - break; - } - - if (!item.ChannelId.Equals(Guid.Empty)) - { - var channel = _libraryManager.GetItemById(item.ChannelId); - result.ChannelName = channel?.Name; - } - - return result; - } - - private void SetThumbImageInfo(SearchHint hint, BaseItem item) - { - var itemWithImage = item.HasImage(ImageType.Thumb) ? item : null; - - if (itemWithImage == null && item is Episode) - { - itemWithImage = GetParentWithImage(item, ImageType.Thumb); - } - - if (itemWithImage == null) - { - itemWithImage = GetParentWithImage(item, ImageType.Thumb); - } - - if (itemWithImage != null) - { - var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Thumb); - - if (tag != null) - { - hint.ThumbImageTag = tag; - hint.ThumbImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); - } - } - } - - private void SetBackdropImageInfo(SearchHint hint, BaseItem item) - { - var itemWithImage = (item.HasImage(ImageType.Backdrop) ? item : null) - ?? GetParentWithImage(item, ImageType.Backdrop); - - if (itemWithImage != null) - { - var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Backdrop); - - if (tag != null) - { - hint.BackdropImageTag = tag; - hint.BackdropImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture); - } - } - } - - private T GetParentWithImage(BaseItem item, ImageType type) - where T : BaseItem - { - return item.GetParents().OfType().FirstOrDefault(i => i.HasImage(type)); - } - } -} -- cgit v1.2.3 From 7fa374f8a2560cd1c58584b3d5b0567c91ef4138 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 7 Jun 2020 15:41:49 +0200 Subject: Move Split method from BaseJellyfinApiController.cs to RequestHelpers.cs --- Jellyfin.Api/BaseJellyfinApiController.cs | 18 ----------------- Jellyfin.Api/Controllers/SearchController.cs | 7 ++++--- Jellyfin.Api/Helpers/RequestHelpers.cs | 29 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 Jellyfin.Api/Helpers/RequestHelpers.cs (limited to 'Jellyfin.Api/BaseJellyfinApiController.cs') diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 6a9e48f8d..22b9c3fd6 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -10,23 +10,5 @@ namespace Jellyfin.Api [Route("[controller]")] public class BaseJellyfinApiController : ControllerBase { - /// - /// Splits a string at a seperating character into an array of substrings. - /// - /// The string to split. - /// The char that seperates the substrings. - /// Option to remove empty substrings from the array. - /// An array of the substrings. - internal static string[] Split(string value, char separator, bool removeEmpty) - { - if (string.IsNullOrWhiteSpace(value)) - { - return Array.Empty(); - } - - return removeEmpty - ? value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) - : value.Split(separator); - } } } diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 2ee3ef25b..b6178e121 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; +using Jellyfin.Api.Helpers; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -105,9 +106,9 @@ namespace Jellyfin.Api.Controllers IncludeStudios = includeStudios, StartIndex = startIndex, UserId = userId, - IncludeItemTypes = Split(includeItemTypes, ',', true), - ExcludeItemTypes = Split(excludeItemTypes, ',', true), - MediaTypes = Split(mediaTypes, ',', true), + IncludeItemTypes = RequestHelpers.Split(includeItemTypes, ',', true), + ExcludeItemTypes = RequestHelpers.Split(excludeItemTypes, ',', true), + MediaTypes = RequestHelpers.Split(mediaTypes, ',', true), ParentId = parentId, IsKids = isKids, diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs new file mode 100644 index 000000000..6c661e2d3 --- /dev/null +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -0,0 +1,29 @@ +using System; + +namespace Jellyfin.Api.Helpers +{ + /// + /// Request Extensions. + /// + public static class RequestHelpers + { + /// + /// Splits a string at a seperating character into an array of substrings. + /// + /// The string to split. + /// The char that seperates the substrings. + /// Option to remove empty substrings from the array. + /// An array of the substrings. + internal static string[] Split(string value, char separator, bool removeEmpty) + { + if (string.IsNullOrWhiteSpace(value)) + { + return Array.Empty(); + } + + return removeEmpty + ? value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) + : value.Split(separator); + } + } +} -- cgit v1.2.3 From ec3e15db5789b6218482beb488433f41f9a0d8ba Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 13 Jun 2020 13:11:41 -0600 Subject: Fix merge and build --- Jellyfin.Api/BaseJellyfinApiController.cs | 4 -- Jellyfin.Api/Controllers/ActivityLogController.cs | 11 +++- .../ConfigurationDtos/MediaEncoderPathDto.cs | 6 +- .../Extensions/ApiServiceCollectionExtensions.cs | 2 +- .../Formatters/CamelCaseJsonProfileFormatter.cs | 2 +- .../Formatters/PascalCaseJsonProfileFormatter.cs | 2 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 4 -- MediaBrowser.Api/System/ActivityLogService.cs | 66 ---------------------- .../JsonNonStringKeyDictionaryConverter.cs | 26 +++++---- .../JsonNonStringKeyDictionaryConverterFactory.cs | 5 +- MediaBrowser.Common/Json/JsonDefaults.cs | 30 +++++----- 11 files changed, 46 insertions(+), 112 deletions(-) delete mode 100644 MediaBrowser.Api/System/ActivityLogService.cs (limited to 'Jellyfin.Api/BaseJellyfinApiController.cs') diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 615f330a4..a34f9eb62 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -1,8 +1,4 @@ -<<<<<<< HEAD -using System; -======= using System.Net.Mime; ->>>>>>> origin/master using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api diff --git a/Jellyfin.Api/Controllers/ActivityLogController.cs b/Jellyfin.Api/Controllers/ActivityLogController.cs index 8d37a8373..895d9f719 100644 --- a/Jellyfin.Api/Controllers/ActivityLogController.cs +++ b/Jellyfin.Api/Controllers/ActivityLogController.cs @@ -1,6 +1,10 @@ +#nullable enable +#pragma warning disable CA1801 + using System; -using System.Globalization; +using System.Linq; using Jellyfin.Api.Constants; +using Jellyfin.Data.Entities; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; @@ -44,7 +48,10 @@ namespace Jellyfin.Api.Controllers [FromQuery] DateTime? minDate, bool? hasUserId) { - return _activityManager.GetActivityLogEntries(minDate, hasUserId, startIndex, limit); + var filterFunc = new Func, IQueryable>( + entries => entries.Where(entry => entry.DateCreated >= minDate)); + + return _activityManager.GetPagedResult(filterFunc, startIndex, limit); } } } diff --git a/Jellyfin.Api/Models/ConfigurationDtos/MediaEncoderPathDto.cs b/Jellyfin.Api/Models/ConfigurationDtos/MediaEncoderPathDto.cs index b05e0cdf5..3706a11e3 100644 --- a/Jellyfin.Api/Models/ConfigurationDtos/MediaEncoderPathDto.cs +++ b/Jellyfin.Api/Models/ConfigurationDtos/MediaEncoderPathDto.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace Jellyfin.Api.Models.ConfigurationDtos { /// @@ -8,11 +10,11 @@ namespace Jellyfin.Api.Models.ConfigurationDtos /// /// Gets or sets media encoder path. /// - public string Path { get; set; } + public string Path { get; set; } = null!; /// /// Gets or sets media encoder path type. /// - public string PathType { get; set; } + public string PathType { get; set; } = null!; } } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 86d547af0..9cdaa0eb1 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -93,7 +93,7 @@ namespace Jellyfin.Server.Extensions .AddJsonOptions(options => { // Update all properties that are set in JsonDefaults - var jsonOptions = JsonDefaults.PascalCase; + var jsonOptions = JsonDefaults.GetPascalCaseOptions(); // From JsonDefaults options.JsonSerializerOptions.ReadCommentHandling = jsonOptions.ReadCommentHandling; diff --git a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs index 989c8ecea..9b347ae2c 100644 --- a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs +++ b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Server.Formatters /// /// Initializes a new instance of the class. /// - public CamelCaseJsonProfileFormatter() : base(JsonDefaults.CamelCase) + public CamelCaseJsonProfileFormatter() : base(JsonDefaults.GetCamelCaseOptions()) { SupportedMediaTypes.Clear(); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;profile=\"CamelCase\"")); diff --git a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs index 69963b3fb..0024708ba 100644 --- a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs +++ b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Server.Formatters /// /// Initializes a new instance of the class. /// - public PascalCaseJsonProfileFormatter() : base(JsonDefaults.PascalCase) + public PascalCaseJsonProfileFormatter() : base(JsonDefaults.GetPascalCaseOptions()) { SupportedMediaTypes.Clear(); // Add application/json for default formatter diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 0b0c5cc9f..d703bdb05 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -14,10 +14,6 @@ - - - - netstandard2.1 false diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs deleted file mode 100644 index a6bacad4f..000000000 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.System -{ - [Route("/System/ActivityLog/Entries", "GET", Summary = "Gets activity log entries")] - public class GetActivityLogs : IReturn> - { - /// - /// Skips over a given number of items within the results. Use for paging. - /// - /// The start index. - [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? StartIndex { get; set; } - - /// - /// The maximum number of items to return - /// - /// The limit. - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MinDate { get; set; } - - public bool? HasUserId { get; set; } - } - - [Authenticated(Roles = "Admin")] - public class ActivityLogService : BaseApiService - { - private readonly IActivityManager _activityManager; - - public ActivityLogService( - ILogger logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IActivityManager activityManager) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _activityManager = activityManager; - } - - public object Get(GetActivityLogs request) - { - DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? - (DateTime?)null : - DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - - var filterFunc = new Func, IQueryable>( - entries => entries.Where(entry => entry.DateCreated >= minDate)); - - var result = _activityManager.GetPagedResult(filterFunc, request.StartIndex, request.Limit); - - return ToOptimizedResult(result); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs index 636ef5372..8053461f0 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverter.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Json.Converters /// The type to convert. /// The json serializer options. /// Typed dictionary. - /// + /// Dictionary key type not supported. public override IDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]); @@ -38,24 +38,24 @@ namespace MediaBrowser.Common.Json.Converters CultureInfo.CurrentCulture); var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null); var parse = typeof(TKey).GetMethod( - "Parse", - 0, - BindingFlags.Public | BindingFlags.Static, - null, - CallingConventions.Any, - new[] { typeof(string) }, + "Parse", + 0, + BindingFlags.Public | BindingFlags.Static, + null, + CallingConventions.Any, + new[] { typeof(string) }, null); if (parse == null) { throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary is not supported."); } - + while (enumerator.MoveNext()) { var element = (KeyValuePair)enumerator.Current; - instance.Add((TKey)parse.Invoke(null, new[] { (object?) element.Key }), element.Value); + instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value); } - + return instance; } @@ -70,8 +70,12 @@ namespace MediaBrowser.Common.Json.Converters var convertedDictionary = new Dictionary(value.Count); foreach (var (k, v) in value) { - convertedDictionary[k?.ToString()] = v; + if (k != null) + { + convertedDictionary[k.ToString()] = v; + } } + JsonSerializer.Serialize(writer, convertedDictionary, options); } } diff --git a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs index d9795a189..52f360740 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNonStringKeyDictionaryConverterFactory.cs @@ -22,18 +22,17 @@ namespace MediaBrowser.Common.Json.Converters /// Conversion ability. public override bool CanConvert(Type typeToConvert) { - if (!typeToConvert.IsGenericType) { return false; } - + // Let built in converter handle string keys if (typeToConvert.GenericTypeArguments[0] == typeof(string)) { return false; } - + // Only support objects that implement IDictionary return typeToConvert.GetInterface(nameof(IDictionary)) != null; } diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index f38e2893e..adc15123b 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Common.Json /// When changing these options, update /// Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs /// -> AddJellyfinApi - /// -> AddJsonOptions + /// -> AddJsonOptions. /// /// The default options. public static JsonSerializerOptions GetOptions() @@ -33,31 +33,27 @@ namespace MediaBrowser.Common.Json return options; } - + /// - /// Gets CamelCase json options. + /// Gets camelCase json options. /// - public static JsonSerializerOptions CamelCase + /// The camelCase options. + public static JsonSerializerOptions GetCamelCaseOptions() { - get - { - var options = GetOptions(); - options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - return options; - } + var options = GetOptions(); + options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + return options; } /// /// Gets PascalCase json options. /// - public static JsonSerializerOptions PascalCase + /// The PascalCase options. + public static JsonSerializerOptions GetPascalCaseOptions() { - get - { - var options = GetOptions(); - options.PropertyNamingPolicy = null; - return options; - } + var options = GetOptions(); + options.PropertyNamingPolicy = null; + return options; } } } -- cgit v1.2.3