diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 50c4ad14fc..945e803b2a 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -1,4 +1,7 @@ -using MediaBrowser.Controller.Activity; +using MediaBrowser.Api.Movies; +using MediaBrowser.Api.Music; +using MediaBrowser.Controller.Activity; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -9,7 +12,9 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.TV; using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; @@ -238,6 +243,12 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } + [Route("/Items/{Id}/Similar", "GET", Summary = "Downloads item media")] + [Authenticated(Roles = "download")] + public class GetSimilarItems : BaseGetSimilarItemsFromItem + { + } + /// /// Class LibraryService /// @@ -257,12 +268,14 @@ namespace MediaBrowser.Api.Library private readonly IActivityManager _activityManager; private readonly ILocalizationManager _localization; private readonly ILiveTvManager _liveTv; + private readonly IChannelManager _channelManager; + private readonly ITVSeriesManager _tvManager; /// /// Initializes a new instance of the class. /// public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv) + IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager) { _itemRepo = itemRepo; _libraryManager = libraryManager; @@ -273,6 +286,115 @@ namespace MediaBrowser.Api.Library _activityManager = activityManager; _localization = localization; _liveTv = liveTv; + _channelManager = channelManager; + _tvManager = tvManager; + } + + public object Get(GetSimilarItems request) + { + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + + var item = string.IsNullOrEmpty(request.Id) ? + (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : + _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + + if (item is Game) + { + return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + { + AuthorizationContext = AuthorizationContext, + Logger = Logger, + Request = Request, + SessionContext = SessionContext, + ResultFactory = ResultFactory + + }.Get(new GetSimilarGames + { + Fields = request.Fields, + Id = request.Id, + Limit = request.Limit, + UserId = request.UserId + }); + } + if (item is MusicAlbum) + { + return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + { + AuthorizationContext = AuthorizationContext, + Logger = Logger, + Request = Request, + SessionContext = SessionContext, + ResultFactory = ResultFactory + + }.Get(new GetSimilarAlbums + { + Fields = request.Fields, + Id = request.Id, + Limit = request.Limit, + UserId = request.UserId + }); + } + if (item is MusicArtist) + { + return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService) + { + AuthorizationContext = AuthorizationContext, + Logger = Logger, + Request = Request, + SessionContext = SessionContext, + ResultFactory = ResultFactory + + }.Get(new GetSimilarArtists + { + Fields = request.Fields, + Id = request.Id, + Limit = request.Limit, + UserId = request.UserId + }); + } + + var program = item as IHasProgramAttributes; + var channelItem = item as ChannelVideoItem; + + if (item is Movie || (program != null && program.IsMovie) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Movie && channelItem.ContentType == ChannelMediaContentType.MovieExtra)) + { + return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _channelManager) + { + AuthorizationContext = AuthorizationContext, + Logger = Logger, + Request = Request, + SessionContext = SessionContext, + ResultFactory = ResultFactory + + }.Get(new GetSimilarMovies + { + Fields = request.Fields, + Id = request.Id, + Limit = request.Limit, + UserId = request.UserId + }); + } + + if (item is Series || (program != null && program.IsSeries) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Episode)) + { + return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager) + { + AuthorizationContext = AuthorizationContext, + Logger = Logger, + Request = Request, + SessionContext = SessionContext, + ResultFactory = ResultFactory + + }.Get(new GetSimilarShows + { + Fields = request.Fields, + Id = request.Id, + Limit = request.Limit, + UserId = request.UserId + }); + } + + return new ItemsResult(); } public object Get(GetMediaFolders request) diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 97e9aa9c8f..fe8bae1a51 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -28,6 +28,14 @@ namespace MediaBrowser.Api.Movies { } + /// + /// Class GetSimilarTrailers + /// + [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] + public class GetSimilarTrailers : BaseGetSimilarItemsFromItem + { + } + [Route("/Movies/Recommendations", "GET", Summary = "Gets movie recommendations")] public class GetMovieRecommendations : IReturn, IHasItemFields { @@ -117,6 +125,17 @@ namespace MediaBrowser.Api.Movies return ToOptimizedSerializedResultUsingCache(result); } + public async Task Get(GetSimilarTrailers request) + { + var result = await GetSimilarItemsResult( + // Strip out secondary versions + request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue, + + SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + + return ToOptimizedSerializedResultUsingCache(result); + } + public async Task Get(GetMovieRecommendations request) { var user = _userManager.GetUserById(request.UserId); @@ -126,7 +145,7 @@ namespace MediaBrowser.Api.Movies movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies); var listEligibleForCategories = new List(); - var listEligibleForSuggestion = new List (); + var listEligibleForSuggestion = new List(); var list = movies.ToList(); @@ -159,7 +178,7 @@ namespace MediaBrowser.Api.Movies var dtoOptions = GetDtoOptions(request); dtoOptions.Fields = request.GetItemFields().ToList(); - + var result = GetRecommendationCategories(user, listEligibleForCategories, listEligibleForSuggestion, request.CategoryLimit, request.ItemLimit, dtoOptions); return ToOptimizedResult(result); @@ -174,14 +193,14 @@ namespace MediaBrowser.Api.Movies _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); Func filter = i => i.Id != item.Id && includeInSearch(i); - + var inputItems = user == null ? _libraryManager.RootFolder.GetRecursiveChildren(filter) : user.RootFolder.GetRecursiveChildren(user, filter); var list = inputItems.ToList(); - if (item is Movie && user != null && user.Configuration.IncludeTrailersInSuggestions) + if (user != null && user.Configuration.IncludeTrailersInSuggestions) { var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery { @@ -224,7 +243,7 @@ namespace MediaBrowser.Api.Movies } var dtoOptions = GetDtoOptions(request); - + var result = new ItemsResult { Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(), diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs index 0847fc7ed2..ed197911a0 100644 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -2,15 +2,12 @@ using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack; -using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -18,14 +15,6 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Movies { - /// - /// Class GetSimilarTrailers - /// - [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] - public class GetSimilarTrailers : BaseGetSimilarItemsFromItem - { - } - [Route("/Trailers", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] public class Getrailers : BaseItemsRequest, IReturn { @@ -51,7 +40,6 @@ namespace MediaBrowser.Api.Movies /// private readonly ILibraryManager _libraryManager; - private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; private readonly IChannelManager _channelManager; @@ -61,40 +49,15 @@ namespace MediaBrowser.Api.Movies /// The user manager. /// The user data repository. /// The library manager. - public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IChannelManager channelManager) + public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, IChannelManager channelManager) { _userManager = userManager; _userDataRepository = userDataRepository; _libraryManager = libraryManager; - _itemRepo = itemRepo; _dtoService = dtoService; _channelManager = channelManager; } - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetSimilarTrailers request) - { - var dtoOptions = GetDtoOptions(request); - - var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, - _itemRepo, - _libraryManager, - _userDataRepository, - _dtoService, - Logger, - - // Strip out secondary versions - request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue, - - SimilarItemsHelper.GetSimiliarityScore); - - return ToOptimizedSerializedResultUsingCache(result); - } - public async Task Get(Getrailers request) { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index ea87c3ad37..548598d429 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Querying; using ServiceStack; using System; using System.Collections.Generic; @@ -16,6 +17,11 @@ namespace MediaBrowser.Api.Music { } + [Route("/Artists/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")] + public class GetSimilarArtists : BaseGetSimilarItemsFromItem + { + } + [Authenticated] public class AlbumsService : BaseApiService { @@ -44,6 +50,17 @@ namespace MediaBrowser.Api.Music _dtoService = dtoService; } + public object Get(GetSimilarArtists request) + { + var result = GetSimilarItemsResult( + + request, + + SimilarItemsHelper.GetSimiliarityScore); + + return ToOptimizedSerializedResultUsingCache(result); + } + /// /// Gets the specified request. /// @@ -65,6 +82,39 @@ namespace MediaBrowser.Api.Music return ToOptimizedSerializedResultUsingCache(result); } + private ItemsResult GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func, List, BaseItem, int> getSimilarityScore) + { + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + + var item = string.IsNullOrEmpty(request.Id) ? + (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : + _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + + var inputItems = _libraryManager.GetArtists(user.RootFolder.GetRecursiveChildren(user, i => i is IHasArtist).OfType()); + + var list = inputItems.ToList(); + + var items = SimilarItemsHelper.GetSimilaritems(item, _libraryManager, list, getSimilarityScore).ToList(); + + IEnumerable returnItems = items; + + if (request.Limit.HasValue) + { + returnItems = returnItems.Take(request.Limit.Value); + } + + var dtoOptions = GetDtoOptions(request); + + var result = new ItemsResult + { + Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(), + + TotalRecordCount = items.Count + }; + + return result; + } + /// /// Gets the album similarity score. /// diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 9e56907dae..d383bd0adb 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -1,12 +1,10 @@ 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.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Querying; using ServiceStack; using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs index 391c8f7a7e..9938a44894 100644 --- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs +++ b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs @@ -11,6 +11,7 @@ namespace MediaBrowser.Controller.Entities bool IsKids { get; set; } bool IsRepeat { get; set; } bool? IsHD { get; set; } + bool IsSeries { get; set; } bool IsLive { get; set; } bool IsPremiere { get; set; } ProgramAudio? Audio { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs index 9fc60beb9b..ba0b82a0b6 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs @@ -37,7 +37,6 @@ namespace MediaBrowser.Controller.LiveTv string ExternalId { get; set; } string EpisodeTitle { get; set; } - bool IsSeries { get; set; } string SeriesTimerId { get; set; } RecordingStatus Status { get; set; } DateTime? EndDate { get; set; } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index e52e7535b6..6d441ccf89 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -353,39 +353,7 @@ namespace MediaBrowser.Model.ApiClient /// The query. /// The cancellation token. /// Task{ItemsResult}. - Task GetSimilarMoviesAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets the similar trailers async. - /// - /// The query. - /// The cancellation token. - /// Task{ItemsResult}. - Task GetSimilarTrailersAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets the similar series async. - /// - /// The query. - /// The cancellation token. - /// Task{ItemsResult}. - Task GetSimilarSeriesAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets the similar albums async. - /// - /// The query. - /// The cancellation token. - /// Task{ItemsResult}. - Task GetSimilarAlbumsAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets the similar games async. - /// - /// The query. - /// The cancellation token. - /// Task{ItemsResult}. - Task GetSimilarGamesAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken)); + Task GetSimilarItemsAsync(SimilarItemsQuery query, CancellationToken cancellationToken = default(CancellationToken)); /// /// Gets the people async. @@ -442,20 +410,6 @@ namespace MediaBrowser.Model.ApiClient /// Task{ItemsResult}. Task GetGenresAsync(ItemsByNameQuery query); - /// - /// Gets the music genres async. - /// - /// The query. - /// Task{ItemsResult}. - Task GetMusicGenresAsync(ItemsByNameQuery query); - - /// - /// Gets the game genres async. - /// - /// The query. - /// Task{ItemsResult}. - Task GetGameGenresAsync(ItemsByNameQuery query); - /// /// Gets the studios async. /// diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index aa68e09a0f..ac8fa3370b 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -1081,6 +1081,15 @@ namespace MediaBrowser.Model.Dto get { return StringHelper.EqualsIgnoreCase(Type, "Studio"); } } + [IgnoreDataMember] + public bool SupportsSimilarItems + { + get + { + return IsType("Movie") || IsType("Series") || IsType("MusicAlbum") || IsType("MusicArtist") || IsType("Program") || IsType("Recording") || IsType("ChannelVideoItem") || IsType("Game"); + } + } + /// /// Occurs when [property changed]. /// diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index ebb5037d04..200c6c9a6e 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -517,7 +517,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recordPath = Path.Combine(recordPath, "TV", _fileSystem.GetValidFilename(info.Name)); } - recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info))); + var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)) + ".ts"; + + recordPath = Path.Combine(recordPath, recordingFileName); Directory.CreateDirectory(Path.GetDirectoryName(recordPath)); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.ProgramId, info.Id, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 1b8cd2e618..f8f65c6d52 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -37,7 +37,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { if (info == null) { - return timer.ProgramId + ".ts"; + return timer.ProgramId; } var name = info.Name; @@ -52,7 +52,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { name += " " + info.OriginalAirDate.Value.ToString("yyyy-MM-dd"); } - else if (!string.IsNullOrWhiteSpace(info.EpisodeTitle)) + + if (!string.IsNullOrWhiteSpace(info.EpisodeTitle)) { name += " " + info.EpisodeTitle; } @@ -63,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV name += " (" + info.ProductionYear + ")"; } - return name + ".ts"; + return name; } } }