< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.SearchController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/SearchController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 98
Coverable lines: 98
Total lines: 266
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 44
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GetSearchHints(...)100%210%
GetSearchHintResult(...)0%600240%
SetThumbImageInfo(...)0%156120%
SetBackdropImageInfo(...)0%7280%
GetParentWithImage(...)100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Api/Controllers/SearchController.cs

#LineLine coverage
 1using System;
 2using System.ComponentModel;
 3using System.ComponentModel.DataAnnotations;
 4using System.Globalization;
 5using System.Linq;
 6using Jellyfin.Api.Helpers;
 7using Jellyfin.Api.ModelBinders;
 8using Jellyfin.Data.Enums;
 9using Jellyfin.Extensions;
 10using MediaBrowser.Controller.Drawing;
 11using MediaBrowser.Controller.Dto;
 12using MediaBrowser.Controller.Entities;
 13using MediaBrowser.Controller.Entities.Audio;
 14using MediaBrowser.Controller.Entities.TV;
 15using MediaBrowser.Controller.Library;
 16using MediaBrowser.Controller.LiveTv;
 17using MediaBrowser.Model.Entities;
 18using MediaBrowser.Model.Search;
 19using Microsoft.AspNetCore.Authorization;
 20using Microsoft.AspNetCore.Http;
 21using Microsoft.AspNetCore.Mvc;
 22
 23namespace Jellyfin.Api.Controllers;
 24
 25/// <summary>
 26/// Search controller.
 27/// </summary>
 28[Route("Search/Hints")]
 29[Authorize]
 30public class SearchController : BaseJellyfinApiController
 31{
 32    private readonly ISearchEngine _searchEngine;
 33    private readonly ILibraryManager _libraryManager;
 34    private readonly IDtoService _dtoService;
 35    private readonly IImageProcessor _imageProcessor;
 36
 37    /// <summary>
 38    /// Initializes a new instance of the <see cref="SearchController"/> class.
 39    /// </summary>
 40    /// <param name="searchEngine">Instance of <see cref="ISearchEngine"/> interface.</param>
 41    /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param>
 42    /// <param name="dtoService">Instance of <see cref="IDtoService"/> interface.</param>
 43    /// <param name="imageProcessor">Instance of <see cref="IImageProcessor"/> interface.</param>
 044    public SearchController(
 045        ISearchEngine searchEngine,
 046        ILibraryManager libraryManager,
 047        IDtoService dtoService,
 048        IImageProcessor imageProcessor)
 49    {
 050        _searchEngine = searchEngine;
 051        _libraryManager = libraryManager;
 052        _dtoService = dtoService;
 053        _imageProcessor = imageProcessor;
 054    }
 55
 56    /// <summary>
 57    /// Gets the search hint result.
 58    /// </summary>
 59    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 60    /// <param name="limit">Optional. The maximum number of records to return.</param>
 61    /// <param name="userId">Optional. Supply a user id to search within a user's library or omit to search all.</param>
 62    /// <param name="searchTerm">The search term to filter on.</param>
 63    /// <param name="includeItemTypes">If specified, only results with the specified item types are returned. This allow
 64    /// <param name="excludeItemTypes">If specified, results with these item types are filtered out. This allows multipl
 65    /// <param name="mediaTypes">If specified, only results with the specified media types are returned. This allows mul
 66    /// <param name="parentId">If specified, only children of the parent are returned.</param>
 67    /// <param name="isMovie">Optional filter for movies.</param>
 68    /// <param name="isSeries">Optional filter for series.</param>
 69    /// <param name="isNews">Optional filter for news.</param>
 70    /// <param name="isKids">Optional filter for kids.</param>
 71    /// <param name="isSports">Optional filter for sports.</param>
 72    /// <param name="includePeople">Optional filter whether to include people.</param>
 73    /// <param name="includeMedia">Optional filter whether to include media.</param>
 74    /// <param name="includeGenres">Optional filter whether to include genres.</param>
 75    /// <param name="includeStudios">Optional filter whether to include studios.</param>
 76    /// <param name="includeArtists">Optional filter whether to include artists.</param>
 77    /// <response code="200">Search hint returned.</response>
 78    /// <returns>An <see cref="SearchHintResult"/> with the results of the search.</returns>
 79    [HttpGet]
 80    [Description("Gets search hints based on a search term")]
 81    [ProducesResponseType(StatusCodes.Status200OK)]
 82    public ActionResult<SearchHintResult> GetSearchHints(
 83        [FromQuery] int? startIndex,
 84        [FromQuery] int? limit,
 85        [FromQuery] Guid? userId,
 86        [FromQuery, Required] string searchTerm,
 87        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
 88        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
 89        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
 90        [FromQuery] Guid? parentId,
 91        [FromQuery] bool? isMovie,
 92        [FromQuery] bool? isSeries,
 93        [FromQuery] bool? isNews,
 94        [FromQuery] bool? isKids,
 95        [FromQuery] bool? isSports,
 96        [FromQuery] bool includePeople = true,
 97        [FromQuery] bool includeMedia = true,
 98        [FromQuery] bool includeGenres = true,
 99        [FromQuery] bool includeStudios = true,
 100        [FromQuery] bool includeArtists = true)
 101    {
 0102        userId = RequestHelpers.GetUserId(User, userId);
 0103        var result = _searchEngine.GetSearchHints(new SearchQuery
 0104        {
 0105            Limit = limit,
 0106            SearchTerm = searchTerm,
 0107            IncludeArtists = includeArtists,
 0108            IncludeGenres = includeGenres,
 0109            IncludeMedia = includeMedia,
 0110            IncludePeople = includePeople,
 0111            IncludeStudios = includeStudios,
 0112            StartIndex = startIndex,
 0113            UserId = userId.Value,
 0114            IncludeItemTypes = includeItemTypes,
 0115            ExcludeItemTypes = excludeItemTypes,
 0116            MediaTypes = mediaTypes,
 0117            ParentId = parentId,
 0118
 0119            IsKids = isKids,
 0120            IsMovie = isMovie,
 0121            IsNews = isNews,
 0122            IsSeries = isSeries,
 0123            IsSports = isSports
 0124        });
 125
 0126        return new SearchHintResult(result.Items.Select(GetSearchHintResult).ToArray(), result.TotalRecordCount);
 127    }
 128
 129    /// <summary>
 130    /// Gets the search hint result.
 131    /// </summary>
 132    /// <param name="hintInfo">The hint info.</param>
 133    /// <returns>SearchHintResult.</returns>
 134    private SearchHint GetSearchHintResult(SearchHintInfo hintInfo)
 135    {
 0136        var item = hintInfo.Item;
 137
 0138        var result = new SearchHint
 0139        {
 0140            Name = item.Name,
 0141            IndexNumber = item.IndexNumber,
 0142            ParentIndexNumber = item.ParentIndexNumber,
 0143            Id = item.Id,
 0144            Type = item.GetBaseItemKind(),
 0145            MediaType = item.MediaType,
 0146            MatchedTerm = hintInfo.MatchedTerm,
 0147            RunTimeTicks = item.RunTimeTicks,
 0148            ProductionYear = item.ProductionYear,
 0149            ChannelId = item.ChannelId,
 0150            EndDate = item.EndDate
 0151        };
 152
 153#pragma warning disable CS0618
 154        // Kept for compatibility with older clients
 0155        result.ItemId = result.Id;
 156#pragma warning restore CS0618
 157
 0158        if (item.IsFolder)
 159        {
 0160            result.IsFolder = true;
 161        }
 162
 0163        var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
 164
 0165        if (primaryImageTag is not null)
 166        {
 0167            result.PrimaryImageTag = primaryImageTag;
 0168            result.PrimaryImageAspectRatio = _dtoService.GetPrimaryImageAspectRatio(item);
 169        }
 170
 0171        SetThumbImageInfo(result, item);
 0172        SetBackdropImageInfo(result, item);
 173
 174        switch (item)
 175        {
 176            case IHasSeries hasSeries:
 0177                result.Series = hasSeries.SeriesName;
 0178                break;
 179            case LiveTvProgram program:
 0180                result.StartDate = program.StartDate;
 0181                break;
 182            case Series series:
 0183                if (series.Status.HasValue)
 184                {
 0185                    result.Status = series.Status.Value.ToString();
 186                }
 187
 0188                break;
 189            case MusicAlbum album:
 0190                result.Artists = album.Artists;
 0191                result.AlbumArtist = album.AlbumArtist;
 0192                break;
 193            case Audio song:
 0194                result.AlbumArtist = song.AlbumArtists?.FirstOrDefault();
 0195                result.Artists = song.Artists;
 196
 0197                MusicAlbum musicAlbum = song.AlbumEntity;
 198
 0199                if (musicAlbum is not null)
 200                {
 0201                    result.Album = musicAlbum.Name;
 0202                    result.AlbumId = musicAlbum.Id;
 203                }
 204                else
 205                {
 0206                    result.Album = song.Album;
 207                }
 208
 209                break;
 210        }
 211
 0212        if (!item.ChannelId.IsEmpty())
 213        {
 0214            var channel = _libraryManager.GetItemById<BaseItem>(item.ChannelId);
 0215            result.ChannelName = channel?.Name;
 216        }
 217
 0218        return result;
 219    }
 220
 221    private void SetThumbImageInfo(SearchHint hint, BaseItem item)
 222    {
 0223        var itemWithImage = item.HasImage(ImageType.Thumb) ? item : null;
 224
 0225        if (itemWithImage is null && item is Episode)
 226        {
 0227            itemWithImage = GetParentWithImage<Series>(item, ImageType.Thumb);
 228        }
 229
 0230        itemWithImage ??= GetParentWithImage<BaseItem>(item, ImageType.Thumb);
 231
 0232        if (itemWithImage is not null)
 233        {
 0234            var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Thumb);
 235
 0236            if (tag is not null)
 237            {
 0238                hint.ThumbImageTag = tag;
 0239                hint.ThumbImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture);
 240            }
 241        }
 0242    }
 243
 244    private void SetBackdropImageInfo(SearchHint hint, BaseItem item)
 245    {
 0246        var itemWithImage = (item.HasImage(ImageType.Backdrop) ? item : null)
 0247            ?? GetParentWithImage<BaseItem>(item, ImageType.Backdrop);
 248
 0249        if (itemWithImage is not null)
 250        {
 0251            var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Backdrop);
 252
 0253            if (tag is not null)
 254            {
 0255                hint.BackdropImageTag = tag;
 0256                hint.BackdropImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture);
 257            }
 258        }
 0259    }
 260
 261    private T? GetParentWithImage<T>(BaseItem item, ImageType type)
 262        where T : BaseItem
 263    {
 0264        return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));
 265    }
 266}