aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2013-08-09 11:55:22 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2013-08-09 11:55:22 -0400
commit2173ab0f9fcabfaf889c2c25755f67cbdfafb021 (patch)
tree210b4f7027e6b5cc4a7d2dd2614b88f6056f62d5
parenta7b07f9631c3028d47e2539859188eacbf8f9905 (diff)
Added instant mixes
-rw-r--r--MediaBrowser.Api/AlbumsService.cs2
-rw-r--r--MediaBrowser.Api/GamesService.cs2
-rw-r--r--MediaBrowser.Api/InstantMixService.cs141
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/MoviesService.cs2
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs26
-rw-r--r--MediaBrowser.Api/TrailersService.cs2
-rw-r--r--MediaBrowser.Api/TvShowsService.cs2
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js44
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
10 files changed, 207 insertions, 17 deletions
diff --git a/MediaBrowser.Api/AlbumsService.cs b/MediaBrowser.Api/AlbumsService.cs
index c1a476e09..cbdf1923b 100644
--- a/MediaBrowser.Api/AlbumsService.cs
+++ b/MediaBrowser.Api/AlbumsService.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Api
{
[Route("/Albums/{Id}/Similar", "GET")]
[Api(Description = "Finds albums similar to a given album.")]
- public class GetSimilarAlbums : BaseGetSimilarItems
+ public class GetSimilarAlbums : BaseGetSimilarItemsFromItem
{
}
diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs
index 0073397dd..d88840e80 100644
--- a/MediaBrowser.Api/GamesService.cs
+++ b/MediaBrowser.Api/GamesService.cs
@@ -10,7 +10,7 @@ namespace MediaBrowser.Api
/// </summary>
[Route("/Games/{Id}/Similar", "GET")]
[Api(Description = "Finds games similar to a given game.")]
- public class GetSimilarGames : BaseGetSimilarItems
+ public class GetSimilarGames : BaseGetSimilarItemsFromItem
{
}
diff --git a/MediaBrowser.Api/InstantMixService.cs b/MediaBrowser.Api/InstantMixService.cs
new file mode 100644
index 000000000..d8a8d7db5
--- /dev/null
+++ b/MediaBrowser.Api/InstantMixService.cs
@@ -0,0 +1,141 @@
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Querying;
+using ServiceStack.ServiceHost;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api
+{
+ [Route("/Songs/{Id}/InstantMix", "GET")]
+ [Api(Description = "Creates an instant playlist based on a given song")]
+ public class GetInstantMixFromSong : BaseGetSimilarItemsFromItem
+ {
+ }
+
+ [Route("/Albums/{Id}/InstantMix", "GET")]
+ [Api(Description = "Creates an instant playlist based on a given album")]
+ public class GetInstantMixFromAlbum : BaseGetSimilarItemsFromItem
+ {
+ }
+
+ [Route("/Artists/{Name}/InstantMix", "GET")]
+ [Api(Description = "Creates an instant playlist based on a given artist")]
+ public class GetInstantMixFromArtist : BaseGetSimilarItems
+ {
+ [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Name { get; set; }
+ }
+
+ [Route("/MusicGenres/{Name}/InstantMix", "GET")]
+ [Api(Description = "Creates an instant playlist based on a music genre")]
+ public class GetInstantMixFromMusicGenre : BaseGetSimilarItems
+ {
+ [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Name { get; set; }
+ }
+
+ public class InstantMixService : BaseApiService
+ {
+ private readonly IUserManager _userManager;
+ private readonly IUserDataRepository _userDataRepository;
+ private readonly ILibraryManager _libraryManager;
+
+ private readonly IItemRepository _itemRepo;
+
+ public InstantMixService(IUserManager userManager, IUserDataRepository userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo)
+ {
+ _userManager = userManager;
+ _userDataRepository = userDataRepository;
+ _libraryManager = libraryManager;
+ _itemRepo = itemRepo;
+ }
+
+ public object Get(GetInstantMixFromSong request)
+ {
+ var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager);
+
+ var result = GetInstantMixResult(request, item.Genres).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetInstantMixFromAlbum request)
+ {
+ var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager);
+
+ var result = GetInstantMixResult(request, item.Genres).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetInstantMixFromMusicGenre request)
+ {
+ var genre = GetMusicGenre(request.Name, _libraryManager).Result;
+
+ var result = GetInstantMixResult(request, new[] { genre.Name }).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetInstantMixFromArtist request)
+ {
+ var artist = GetArtist(request.Name, _libraryManager).Result;
+
+ var genres = _libraryManager.RootFolder
+ .RecursiveChildren
+ .OfType<Audio>()
+ .Where(i => i.HasArtist(artist.Name))
+ .SelectMany(i => i.Genres)
+ .Distinct(StringComparer.OrdinalIgnoreCase);
+
+ var result = GetInstantMixResult(request, genres).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ private async Task<ItemsResult> GetInstantMixResult(BaseGetSimilarItems request, IEnumerable<string> genres)
+ {
+ var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+
+ var fields = request.GetItemFields().ToList();
+
+ var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo);
+
+ var inputItems = user == null
+ ? _libraryManager.RootFolder.RecursiveChildren
+ : user.RootFolder.GetRecursiveChildren(user);
+
+ var genresDictionary = genres.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+
+ var limit = request.Limit.HasValue ? request.Limit.Value * 2 : 100;
+
+ var items = inputItems
+ .OfType<Audio>()
+ .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
+ .OrderByDescending(i => i.Item2)
+ .ThenBy(i => Guid.NewGuid())
+ .Select(i => i.Item1)
+ .Take(limit)
+ .OrderBy(i => Guid.NewGuid())
+ .ToArray();
+
+ var result = new ItemsResult
+ {
+ TotalRecordCount = items.Length
+ };
+
+ var tasks = items.Take(request.Limit ?? items.Length)
+ .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user));
+
+ result.Items = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+ return result;
+ }
+
+ }
+}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index cac11d4c0..2b2812cd9 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -74,6 +74,7 @@
<Compile Include="Images\ImageRequest.cs" />
<Compile Include="Images\ImageService.cs" />
<Compile Include="Images\ImageWriter.cs" />
+ <Compile Include="InstantMixService.cs" />
<Compile Include="ItemRefreshService.cs" />
<Compile Include="ItemUpdateService.cs" />
<Compile Include="LibraryService.cs" />
diff --git a/MediaBrowser.Api/MoviesService.cs b/MediaBrowser.Api/MoviesService.cs
index 166a843dc..5ec9aa103 100644
--- a/MediaBrowser.Api/MoviesService.cs
+++ b/MediaBrowser.Api/MoviesService.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Api
/// </summary>
[Route("/Movies/{Id}/Similar", "GET")]
[Api(Description = "Finds movies and trailers similar to a given movie.")]
- public class GetSimilarMovies : BaseGetSimilarItems
+ public class GetSimilarMovies : BaseGetSimilarItemsFromItem
{
[ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeTrailers { get; set; }
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index 5f5d6a2f3..7af3b399e 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -13,8 +13,18 @@ using System.Linq;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class BaseGetSimilarItems
+ /// Class BaseGetSimilarItemsFromItem
/// </summary>
+ public class BaseGetSimilarItemsFromItem : BaseGetSimilarItems
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
public class BaseGetSimilarItems : IReturn<ItemsResult>
{
/// <summary>
@@ -25,13 +35,6 @@ namespace MediaBrowser.Api
public Guid? UserId { get; set; }
/// <summary>
- /// Gets or sets the id.
- /// </summary>
- /// <value>The id.</value>
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
-
- /// <summary>
/// The maximum number of items to return
/// </summary>
/// <value>The limit.</value>
@@ -71,7 +74,7 @@ namespace MediaBrowser.Api
}).Where(i => i.HasValue).Select(i => i.Value);
}
}
-
+
/// <summary>
/// Class SimilarItemsHelper
/// </summary>
@@ -89,7 +92,7 @@ namespace MediaBrowser.Api
/// <param name="includeInSearch">The include in search.</param>
/// <param name="getSimilarityScore">The get similarity score.</param>
/// <returns>ItemsResult.</returns>
- internal static ItemsResult GetSimilarItemsResult(IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataRepository userDataRepository, ILogger logger, BaseGetSimilarItems request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
+ internal static ItemsResult GetSimilarItemsResult(IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataRepository userDataRepository, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
{
var user = request.UserId.HasValue ? userManager.GetUserById(request.UserId.Value) : null;
@@ -105,7 +108,8 @@ namespace MediaBrowser.Api
? libraryManager.RootFolder.RecursiveChildren
: user.RootFolder.GetRecursiveChildren(user);
- var items = GetSimilaritems(item, inputItems, includeInSearch, getSimilarityScore).ToArray();
+ var items = GetSimilaritems(item, inputItems, includeInSearch, getSimilarityScore)
+ .ToArray();
var result = new ItemsResult
{
diff --git a/MediaBrowser.Api/TrailersService.cs b/MediaBrowser.Api/TrailersService.cs
index 96eb5b6c0..ba544a49b 100644
--- a/MediaBrowser.Api/TrailersService.cs
+++ b/MediaBrowser.Api/TrailersService.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Api
/// </summary>
[Route("/Trailers/{Id}/Similar", "GET")]
[Api(Description = "Finds movies and trailers similar to a given trailer.")]
- public class GetSimilarTrailers : BaseGetSimilarItems
+ public class GetSimilarTrailers : BaseGetSimilarItemsFromItem
{
}
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index 4d277dc38..cdb27fb0c 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -77,7 +77,7 @@ namespace MediaBrowser.Api
[Route("/Shows/{Id}/Similar", "GET")]
[Api(Description = "Finds tv shows similar to a given one.")]
- public class GetSimilarShows : BaseGetSimilarItems
+ public class GetSimilarShows : BaseGetSimilarItemsFromItem
{
}
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index e989d8ff1..a0d4b399d 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -319,6 +319,50 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
});
};
+ self.getInstantMixFromSong = function (itemId, options) {
+
+ var url = self.getUrl("Songs/" + itemId + "/InstantMix", options);
+
+ return self.ajax({
+ type: "GET",
+ url: url,
+ dataType: "json"
+ });
+ };
+
+ self.getInstantMixFromAlbum = function (itemId, options) {
+
+ var url = self.getUrl("Albums/" + itemId + "/InstantMix", options);
+
+ return self.ajax({
+ type: "GET",
+ url: url,
+ dataType: "json"
+ });
+ };
+
+ self.getInstantMixFromArtist = function (name, options) {
+
+ var url = self.getUrl("Artists/" + self.encodeName(name) + "/InstantMix", options);
+
+ return self.ajax({
+ type: "GET",
+ url: url,
+ dataType: "json"
+ });
+ };
+
+ self.getInstantMixFromMusicGenre = function (name, options) {
+
+ var url = self.getUrl("MusicGenres/" + self.encodeName(name) + "/InstantMix", options);
+
+ return self.ajax({
+ type: "GET",
+ url: url,
+ dataType: "json"
+ });
+ };
+
self.getSimilarMovies = function (itemId, options) {
var url = self.getUrl("Movies/" + itemId + "/Similar", options);
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 8fdd0ef05..07f45e242 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.159" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.160" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.56" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.55" targetFramework="net45" />
</packages> \ No newline at end of file