Move AlbumsService to Jellyfin.Api
This commit is contained in:
parent
7c1020cfc1
commit
fa98013621
128
Jellyfin.Api/Controllers/AlbumsController.cs
Normal file
128
Jellyfin.Api/Controllers/AlbumsController.cs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Jellyfin.Api.Extensions;
|
||||||
|
using Jellyfin.Api.Helpers;
|
||||||
|
using MediaBrowser.Controller.Dto;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Controllers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The albums controller.
|
||||||
|
/// </summary>
|
||||||
|
public class AlbumsController : BaseJellyfinApiController
|
||||||
|
{
|
||||||
|
private readonly IUserManager _userManager;
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IDtoService _dtoService;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AlbumsController"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
||||||
|
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
||||||
|
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
|
||||||
|
public AlbumsController(
|
||||||
|
IUserManager userManager,
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
IDtoService dtoService)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
_dtoService = dtoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds albums similar to a given album.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="albumId">The album id.</param>
|
||||||
|
/// <param name="userId">Optional. Filter by user id, and attach user data.</param>
|
||||||
|
/// <param name="excludeArtistIds">Optional. Ids of artists to exclude.</param>
|
||||||
|
/// <param name="limit">Optional. The maximum number of records to return.</param>
|
||||||
|
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with similar albums.</returns>
|
||||||
|
[HttpGet("/Albums/{albumId}/Similar")]
|
||||||
|
public ActionResult<QueryResult<BaseItemDto>> GetSimilarAlbums(
|
||||||
|
[FromRoute] string albumId,
|
||||||
|
[FromQuery] Guid userId,
|
||||||
|
[FromQuery] string excludeArtistIds,
|
||||||
|
[FromQuery] int? limit)
|
||||||
|
{
|
||||||
|
var dtoOptions = new DtoOptions().AddClientFields(Request);
|
||||||
|
|
||||||
|
return SimilarItemsHelper.GetSimilarItemsResult(
|
||||||
|
dtoOptions,
|
||||||
|
_userManager,
|
||||||
|
_libraryManager,
|
||||||
|
_dtoService,
|
||||||
|
userId,
|
||||||
|
albumId,
|
||||||
|
excludeArtistIds,
|
||||||
|
limit,
|
||||||
|
new[] { typeof(MusicAlbum) },
|
||||||
|
GetAlbumSimilarityScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds artists similar to a given artist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="artistId">The artist id.</param>
|
||||||
|
/// <param name="userId">Optional. Filter by user id, and attach user data.</param>
|
||||||
|
/// <param name="excludeArtistIds">Optional. Ids of artists to exclude.</param>
|
||||||
|
/// <param name="limit">Optional. The maximum number of records to return.</param>
|
||||||
|
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with similar artists.</returns>
|
||||||
|
[HttpGet("/Artists/{artistId}/Similar")]
|
||||||
|
public ActionResult<QueryResult<BaseItemDto>> GetSimilarArtists(
|
||||||
|
[FromRoute] string artistId,
|
||||||
|
[FromQuery] Guid userId,
|
||||||
|
[FromQuery] string excludeArtistIds,
|
||||||
|
[FromQuery] int? limit)
|
||||||
|
{
|
||||||
|
var dtoOptions = new DtoOptions().AddClientFields(Request);
|
||||||
|
|
||||||
|
return SimilarItemsHelper.GetSimilarItemsResult(
|
||||||
|
dtoOptions,
|
||||||
|
_userManager,
|
||||||
|
_libraryManager,
|
||||||
|
_dtoService,
|
||||||
|
userId,
|
||||||
|
artistId,
|
||||||
|
excludeArtistIds,
|
||||||
|
limit,
|
||||||
|
new[] { typeof(MusicArtist) },
|
||||||
|
SimilarItemsHelper.GetSimiliarityScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a similairty score of two albums.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item1">The first item.</param>
|
||||||
|
/// <param name="item1People">The item1 people.</param>
|
||||||
|
/// <param name="allPeople">All people.</param>
|
||||||
|
/// <param name="item2">The second item.</param>
|
||||||
|
/// <returns>System.Int32.</returns>
|
||||||
|
private int GetAlbumSimilarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2)
|
||||||
|
{
|
||||||
|
var points = SimilarItemsHelper.GetSimiliarityScore(item1, item1People, allPeople, item2);
|
||||||
|
|
||||||
|
var album1 = (MusicAlbum)item1;
|
||||||
|
var album2 = (MusicAlbum)item2;
|
||||||
|
|
||||||
|
var artists1 = album1
|
||||||
|
.GetAllArtists()
|
||||||
|
.DistinctNames()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var artists2 = new HashSet<string>(
|
||||||
|
album2.GetAllArtists().DistinctNames(),
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
return points + artists1.Where(artists2.Contains).Sum(i => 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
182
Jellyfin.Api/Helpers/SimilarItemsHelper.cs
Normal file
182
Jellyfin.Api/Helpers/SimilarItemsHelper.cs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using MediaBrowser.Controller.Dto;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The similar items helper class.
|
||||||
|
/// </summary>
|
||||||
|
public static class SimilarItemsHelper
|
||||||
|
{
|
||||||
|
internal static QueryResult<BaseItemDto> GetSimilarItemsResult(
|
||||||
|
DtoOptions dtoOptions,
|
||||||
|
IUserManager userManager,
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
IDtoService dtoService,
|
||||||
|
Guid userId,
|
||||||
|
string id,
|
||||||
|
string excludeArtistIds,
|
||||||
|
int? limit,
|
||||||
|
Type[] includeTypes,
|
||||||
|
Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
|
||||||
|
{
|
||||||
|
var user = !userId.Equals(Guid.Empty) ? userManager.GetUserById(userId) : null;
|
||||||
|
|
||||||
|
var item = string.IsNullOrEmpty(id) ?
|
||||||
|
(!userId.Equals(Guid.Empty) ? libraryManager.GetUserRootFolder() :
|
||||||
|
libraryManager.RootFolder) : libraryManager.GetItemById(id);
|
||||||
|
|
||||||
|
var query = new InternalItemsQuery(user)
|
||||||
|
{
|
||||||
|
IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(),
|
||||||
|
Recursive = true,
|
||||||
|
DtoOptions = dtoOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
query.ExcludeArtistIds = RequestHelpers.GetGuids(excludeArtistIds);
|
||||||
|
|
||||||
|
var inputItems = libraryManager.GetItemList(query);
|
||||||
|
|
||||||
|
var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var returnItems = items;
|
||||||
|
|
||||||
|
if (limit.HasValue)
|
||||||
|
{
|
||||||
|
returnItems = returnItems.Take(limit.Value).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
var dtos = dtoService.GetBaseItemDtos(returnItems, dtoOptions, user);
|
||||||
|
|
||||||
|
return new QueryResult<BaseItemDto>
|
||||||
|
{
|
||||||
|
Items = dtos,
|
||||||
|
TotalRecordCount = items.Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the similaritems.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <param name="libraryManager">The library manager.</param>
|
||||||
|
/// <param name="inputItems">The input items.</param>
|
||||||
|
/// <param name="getSimilarityScore">The get similarity score.</param>
|
||||||
|
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||||
|
private static IEnumerable<BaseItem> GetSimilaritems(
|
||||||
|
BaseItem item,
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
IEnumerable<BaseItem> inputItems,
|
||||||
|
Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
|
||||||
|
{
|
||||||
|
var itemId = item.Id;
|
||||||
|
inputItems = inputItems.Where(i => i.Id != itemId);
|
||||||
|
var itemPeople = libraryManager.GetPeople(item);
|
||||||
|
var allPeople = libraryManager.GetPeople(new InternalPeopleQuery
|
||||||
|
{
|
||||||
|
AppearsInItemId = item.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, itemPeople, allPeople, i)))
|
||||||
|
.Where(i => i.Item2 > 2)
|
||||||
|
.OrderByDescending(i => i.Item2)
|
||||||
|
.Select(i => i.Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> GetTags(BaseItem item)
|
||||||
|
{
|
||||||
|
return item.Tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the similiarity score.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item1">The item1.</param>
|
||||||
|
/// <param name="item1People">The item1 people.</param>
|
||||||
|
/// <param name="allPeople">All people.</param>
|
||||||
|
/// <param name="item2">The item2.</param>
|
||||||
|
/// <returns>System.Int32.</returns>
|
||||||
|
internal static int GetSimiliarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2)
|
||||||
|
{
|
||||||
|
var points = 0;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
points += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find common genres
|
||||||
|
points += item1.Genres.Where(i => item2.Genres.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
|
||||||
|
|
||||||
|
// Find common tags
|
||||||
|
points += GetTags(item1).Where(i => GetTags(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
|
||||||
|
|
||||||
|
// Find common studios
|
||||||
|
points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3);
|
||||||
|
|
||||||
|
var item2PeopleNames = allPeople.Where(i => i.ItemId == item2.Id)
|
||||||
|
.Select(i => i.Name)
|
||||||
|
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||||
|
.DistinctNames()
|
||||||
|
.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
points += item1People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i =>
|
||||||
|
{
|
||||||
|
if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (item1.ProductionYear.HasValue && item2.ProductionYear.HasValue)
|
||||||
|
{
|
||||||
|
var diff = Math.Abs(item1.ProductionYear.Value - item2.ProductionYear.Value);
|
||||||
|
|
||||||
|
// Add if they came out within the same decade
|
||||||
|
if (diff < 10)
|
||||||
|
{
|
||||||
|
points += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And more if within five years
|
||||||
|
if (diff < 5)
|
||||||
|
{
|
||||||
|
points += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,132 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
|
||||||
using MediaBrowser.Controller.Dto;
|
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
|
||||||
using MediaBrowser.Controller.Library;
|
|
||||||
using MediaBrowser.Controller.Net;
|
|
||||||
using MediaBrowser.Controller.Persistence;
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Music
|
|
||||||
{
|
|
||||||
[Route("/Albums/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
|
|
||||||
public class GetSimilarAlbums : BaseGetSimilarItemsFromItem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("/Artists/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
|
|
||||||
public class GetSimilarArtists : BaseGetSimilarItemsFromItem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authenticated]
|
|
||||||
public class AlbumsService : BaseApiService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The _user manager
|
|
||||||
/// </summary>
|
|
||||||
private readonly IUserManager _userManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _user data repository
|
|
||||||
/// </summary>
|
|
||||||
private readonly IUserDataManager _userDataRepository;
|
|
||||||
/// <summary>
|
|
||||||
/// The _library manager
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
private readonly IDtoService _dtoService;
|
|
||||||
private readonly IAuthorizationContext _authContext;
|
|
||||||
|
|
||||||
public AlbumsService(
|
|
||||||
ILogger<AlbumsService> logger,
|
|
||||||
IServerConfigurationManager serverConfigurationManager,
|
|
||||||
IHttpResultFactory httpResultFactory,
|
|
||||||
IUserManager userManager,
|
|
||||||
IUserDataManager userDataRepository,
|
|
||||||
ILibraryManager libraryManager,
|
|
||||||
IItemRepository itemRepo,
|
|
||||||
IDtoService dtoService,
|
|
||||||
IAuthorizationContext authContext)
|
|
||||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
|
||||||
{
|
|
||||||
_userManager = userManager;
|
|
||||||
_userDataRepository = userDataRepository;
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_itemRepo = itemRepo;
|
|
||||||
_dtoService = dtoService;
|
|
||||||
_authContext = authContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Get(GetSimilarArtists request)
|
|
||||||
{
|
|
||||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
|
||||||
|
|
||||||
var result = SimilarItemsHelper.GetSimilarItemsResult(
|
|
||||||
dtoOptions,
|
|
||||||
_userManager,
|
|
||||||
_itemRepo,
|
|
||||||
_libraryManager,
|
|
||||||
_userDataRepository,
|
|
||||||
_dtoService,
|
|
||||||
request, new[] { typeof(MusicArtist) },
|
|
||||||
SimilarItemsHelper.GetSimiliarityScore);
|
|
||||||
|
|
||||||
return ToOptimizedResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the specified request.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">The request.</param>
|
|
||||||
/// <returns>System.Object.</returns>
|
|
||||||
public object Get(GetSimilarAlbums request)
|
|
||||||
{
|
|
||||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
|
||||||
|
|
||||||
var result = SimilarItemsHelper.GetSimilarItemsResult(
|
|
||||||
dtoOptions,
|
|
||||||
_userManager,
|
|
||||||
_itemRepo,
|
|
||||||
_libraryManager,
|
|
||||||
_userDataRepository,
|
|
||||||
_dtoService,
|
|
||||||
request, new[] { typeof(MusicAlbum) },
|
|
||||||
GetAlbumSimilarityScore);
|
|
||||||
|
|
||||||
return ToOptimizedResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the album similarity score.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item1">The item1.</param>
|
|
||||||
/// <param name="item1People">The item1 people.</param>
|
|
||||||
/// <param name="allPeople">All people.</param>
|
|
||||||
/// <param name="item2">The item2.</param>
|
|
||||||
/// <returns>System.Int32.</returns>
|
|
||||||
private int GetAlbumSimilarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2)
|
|
||||||
{
|
|
||||||
var points = SimilarItemsHelper.GetSimiliarityScore(item1, item1People, allPeople, item2);
|
|
||||||
|
|
||||||
var album1 = (MusicAlbum)item1;
|
|
||||||
var album2 = (MusicAlbum)item2;
|
|
||||||
|
|
||||||
var artists1 = album1
|
|
||||||
.GetAllArtists()
|
|
||||||
.DistinctNames()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var artists2 = new HashSet<string>(
|
|
||||||
album2.GetAllArtists().DistinctNames(),
|
|
||||||
StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
return points + artists1.Where(artists2.Contains).Sum(i => 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user