diff --git a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs b/Emby.Drawing/ImageMagick/StripCollageBuilder.cs
index a50a75ccd1..7ed0f36e2c 100644
--- a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs
+++ b/Emby.Drawing/ImageMagick/StripCollageBuilder.cs
@@ -285,14 +285,14 @@ namespace Emby.Drawing.ImageMagick
private MagickWand BuildThumbCollageWand(List paths, int width, int height)
{
- var inputPaths = ImageHelpers.ProjectPaths(paths, 8);
+ var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
using (var wandImages = new MagickWand(inputPaths.ToArray()))
{
var wand = new MagickWand(width, height);
wand.OpenImage("gradient:#111111-#111111");
using (var draw = new DrawingWand())
{
- var iSlice = Convert.ToInt32(width * .1166666667);
+ var iSlice = Convert.ToInt32(width * .1166666667 * 2);
int iTrans = Convert.ToInt32(height * .25);
int iHeight = Convert.ToInt32(height * .62);
var horizontalImagePadding = Convert.ToInt32(width * 0.0125);
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index 2ba4f5aab7..970b463cd1 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -189,7 +189,7 @@ namespace Emby.Drawing
dateModified = tuple.Item2;
}
- var originalImageSize = GetImageSize(originalImagePath, dateModified);
+ var originalImageSize = GetImageSize(originalImagePath, dateModified, true);
// Determine the output size based on incoming parameters
var newSize = DrawingUtils.Resize(originalImageSize, options.Width, options.Height, options.MaxWidth, options.MaxHeight);
@@ -363,12 +363,12 @@ namespace Emby.Drawing
/// ImageSize.
public ImageSize GetImageSize(string path)
{
- return GetImageSize(path, File.GetLastWriteTimeUtc(path));
+ return GetImageSize(path, File.GetLastWriteTimeUtc(path), false);
}
public ImageSize GetImageSize(ItemImageInfo info)
{
- return GetImageSize(info.Path, info.DateModified);
+ return GetImageSize(info.Path, info.DateModified, false);
}
///
@@ -376,9 +376,10 @@ namespace Emby.Drawing
///
/// The path.
/// The image date modified.
+ /// if set to true [allow slow method].
/// ImageSize.
/// path
- private ImageSize GetImageSize(string path, DateTime imageDateModified)
+ private ImageSize GetImageSize(string path, DateTime imageDateModified, bool allowSlowMethod)
{
if (string.IsNullOrEmpty(path))
{
@@ -393,7 +394,7 @@ namespace Emby.Drawing
if (!_cachedImagedSizes.TryGetValue(cacheHash, out size))
{
- size = GetImageSizeInternal(path);
+ size = GetImageSizeInternal(path, allowSlowMethod);
_cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size);
}
@@ -405,8 +406,9 @@ namespace Emby.Drawing
/// Gets the image size internal.
///
/// The path.
+ /// if set to true [allow slow method].
/// ImageSize.
- private ImageSize GetImageSizeInternal(string path)
+ private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
{
ImageSize size;
@@ -416,7 +418,11 @@ namespace Emby.Drawing
}
catch
{
- _logger.Info("Failed to read image header for {0}. Doing it the slow way.", path);
+ if (!allowSlowMethod)
+ {
+ throw;
+ }
+ //_logger.Info("Failed to read image header for {0}. Doing it the slow way.", path);
CheckDisposed();
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 54c28d390a..c2b406190f 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -336,12 +336,6 @@ namespace MediaBrowser.Api
if (job.Type != TranscodingJobType.Progressive)
{
timerDuration = 1800000;
-
- // We can really reduce the timeout for apps that are using the newer api
- if (!string.IsNullOrWhiteSpace(job.PlaySessionId))
- {
- timerDuration = 300000;
- }
}
job.PingTimeout = timerDuration;
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index 9f6c07dd21..f266180a49 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
using ServiceStack;
-using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System.Collections.Generic;
using System.IO;
@@ -26,7 +25,7 @@ namespace MediaBrowser.Api
}
[Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
- [Authenticated]
+ [Authenticated(AllowBeforeStartupWizard = true)]
public class GetNamedConfiguration
{
[ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index 73b6573a6c..457b4709bf 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -221,7 +221,9 @@ namespace MediaBrowser.Api
/// IEnumerable{FileSystemEntryInfo}.
private IEnumerable GetFileSystemEntries(GetDirectoryContents request)
{
- var entries = new DirectoryInfo(request.Path).EnumerateFileSystemInfos().Where(i =>
+ // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
+ var entries = new DirectoryInfo(request.Path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
+ .Concat(new DirectoryInfo(request.Path).EnumerateFiles("*", SearchOption.TopDirectoryOnly)).Where(i =>
{
if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
{
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index e8340a1cb5..49dd121baa 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -1,14 +1,20 @@
-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;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+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;
@@ -225,6 +231,17 @@ namespace MediaBrowser.Api.Library
public string TvdbId { get; set; }
}
+ [Route("/Library/Movies/Added", "POST", Summary = "Reports that new movies have been added by an external source")]
+ [Route("/Library/Movies/Updated", "POST", Summary = "Reports that new movies have been added by an external source")]
+ [Authenticated]
+ public class PostUpdatedMovies : IReturnVoid
+ {
+ [ApiMember(Name = "TmdbId", Description = "Tmdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string TmdbId { get; set; }
+ [ApiMember(Name = "ImdbId", Description = "Imdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string ImdbId { get; set; }
+ }
+
[Route("/Items/{Id}/Download", "GET", Summary = "Downloads item media")]
[Authenticated(Roles = "download")]
public class GetDownload
@@ -237,6 +254,12 @@ namespace MediaBrowser.Api.Library
public string Id { get; set; }
}
+ [Route("/Items/{Id}/Similar", "GET", Summary = "Gets similar items")]
+ [Authenticated]
+ public class GetSimilarItems : BaseGetSimilarItemsFromItem
+ {
+ }
+
///
/// Class LibraryService
///
@@ -255,12 +278,16 @@ namespace MediaBrowser.Api.Library
private readonly IAuthorizationContext _authContext;
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
+ private readonly ILiveTvManager _liveTv;
+ private readonly IChannelManager _channelManager;
+ private readonly ITVSeriesManager _tvManager;
+ private readonly ILibraryMonitor _libraryMonitor;
///
/// 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)
+ IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor)
{
_itemRepo = itemRepo;
_libraryManager = libraryManager;
@@ -270,6 +297,117 @@ namespace MediaBrowser.Api.Library
_authContext = authContext;
_activityManager = activityManager;
_localization = localization;
+ _liveTv = liveTv;
+ _channelManager = channelManager;
+ _tvManager = tvManager;
+ _libraryMonitor = libraryMonitor;
+ }
+
+ 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 != null && 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)
@@ -297,7 +435,59 @@ namespace MediaBrowser.Api.Library
public void Post(PostUpdatedSeries request)
{
- Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None));
+ var series = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Series).Name }
+
+ }).Items;
+
+ series = series.Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+
+ if (series.Length > 0)
+ {
+ foreach (var item in series)
+ {
+ _libraryMonitor.ReportFileSystemChanged(item.Path);
+ }
+ }
+ else
+ {
+ Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None));
+ }
+ }
+
+ public void Post(PostUpdatedMovies request)
+ {
+ var movies = _libraryManager.GetItems(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Movie).Name }
+
+ }).Items;
+
+ if (!string.IsNullOrWhiteSpace(request.ImdbId))
+ {
+ movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+ else if (!string.IsNullOrWhiteSpace(request.TmdbId))
+ {
+ movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ }
+ else
+ {
+ movies = new BaseItem[] { };
+ }
+
+ if (movies.Length > 0)
+ {
+ foreach (var item in movies)
+ {
+ _libraryMonitor.ReportFileSystemChanged(item.Path);
+ }
+ }
+ else
+ {
+ Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None));
+ }
}
public object Get(GetDownload request)
@@ -524,7 +714,6 @@ namespace MediaBrowser.Api.Library
public void Delete(DeleteItem request)
{
var item = _libraryManager.GetItemById(request.Id);
-
var auth = _authContext.GetAuthorizationInfo(Request);
var user = _userManager.GetUserById(auth.UserId);
@@ -533,9 +722,16 @@ namespace MediaBrowser.Api.Library
throw new UnauthorizedAccessException();
}
- var task = _libraryManager.DeleteItem(item);
-
- Task.WaitAll(task);
+ if (item is ILiveTvRecording)
+ {
+ var task = _liveTv.DeleteRecording(request.Id);
+ Task.WaitAll(task);
+ }
+ else
+ {
+ var task = _libraryManager.DeleteItem(item);
+ Task.WaitAll(task);
+ }
}
///
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index c474642d5d..07e64c98d4 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Dto;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
@@ -8,6 +10,7 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
@@ -26,7 +29,7 @@ namespace MediaBrowser.Api.LiveTv
[Route("/LiveTv/Channels", "GET", Summary = "Gets available live tv channels.")]
[Authenticated]
- public class GetChannels : IReturn>
+ public class GetChannels : IReturn>, IHasDtoOptions
{
[ApiMember(Name = "Type", Description = "Optional filter by channel type.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public ChannelType? Type { get; set; }
@@ -59,6 +62,30 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableFavoriteSorting", Description = "Incorporate favorite and like status into channel sorting.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool EnableFavoriteSorting { get; set; }
+
+ [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableImages { get; set; }
+
+ [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ImageTypeLimit { get; set; }
+
+ [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string EnableImageTypes { get; set; }
+
+ ///
+ /// Fields to return within the items, in addition to basic information
+ ///
+ /// The fields.
+ [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Fields { get; set; }
+
+ [ApiMember(Name = "AddCurrentProgram", Description = "Optional. Adds current program info to each channel", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool AddCurrentProgram { get; set; }
+
+ public GetChannels()
+ {
+ AddCurrentProgram = true;
+ }
}
[Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")]
@@ -78,7 +105,7 @@ namespace MediaBrowser.Api.LiveTv
[Route("/LiveTv/Recordings", "GET", Summary = "Gets live tv recordings")]
[Authenticated]
- public class GetRecordings : IReturn>
+ public class GetRecordings : IReturn>, IHasDtoOptions
{
[ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ChannelId { get; set; }
@@ -103,6 +130,22 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SeriesTimerId { get; set; }
+
+ [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableImages { get; set; }
+
+ [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ImageTypeLimit { get; set; }
+
+ [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string EnableImageTypes { get; set; }
+
+ ///
+ /// Fields to return within the items, in addition to basic information
+ ///
+ /// The fields.
+ [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Fields { get; set; }
}
[Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")]
@@ -161,7 +204,7 @@ namespace MediaBrowser.Api.LiveTv
[Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
[Authenticated]
- public class GetPrograms : IReturn>
+ public class GetPrograms : IReturn>, IHasDtoOptions
{
[ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string ChannelIds { get; set; }
@@ -187,6 +230,9 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { 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; }
@@ -204,11 +250,27 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string Genres { get; set; }
+
+ [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableImages { get; set; }
+
+ [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ImageTypeLimit { get; set; }
+
+ [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string EnableImageTypes { get; set; }
+
+ ///
+ /// Fields to return within the items, in addition to basic information
+ ///
+ /// The fields.
+ [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Fields { get; set; }
}
[Route("/LiveTv/Programs/Recommended", "GET", Summary = "Gets available live tv epgs..")]
[Authenticated]
- public class GetRecommendedPrograms : IReturn>
+ public class GetRecommendedPrograms : IReturn>, IHasDtoOptions
{
[ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string UserId { get; set; }
@@ -227,6 +289,25 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMovie { get; set; }
+
+ [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsKids { get; set; }
+
+ [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableImages { get; set; }
+
+ [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ImageTypeLimit { get; set; }
+
+ [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string EnableImageTypes { get; set; }
+
+ ///
+ /// Fields to return within the items, in addition to basic information
+ ///
+ /// The fields.
+ [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Fields { get; set; }
}
[Route("/LiveTv/Programs/{Id}", "GET", Summary = "Gets a live tv program")]
@@ -330,15 +411,108 @@ namespace MediaBrowser.Api.LiveTv
public string UserId { get; set; }
}
+ [Route("/LiveTv/TunerHosts", "POST", Summary = "Adds a tuner host")]
+ [Authenticated]
+ public class AddTunerHost : TunerHostInfo, IReturn
+ {
+ }
+
+ [Route("/LiveTv/TunerHosts", "DELETE", Summary = "Deletes a tuner host")]
+ [Authenticated]
+ public class DeleteTunerHost : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Tuner host id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")]
+ [Authenticated(AllowBeforeStartupWizard = true)]
+ public class AddListingProvider : ListingsProviderInfo, IReturn
+ {
+ public bool ValidateLogin { get; set; }
+ public bool ValidateListings { get; set; }
+ }
+
+ [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
+ [Authenticated(AllowBeforeStartupWizard = true)]
+ public class DeleteListingProvider : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")]
+ [Authenticated(AllowBeforeStartupWizard = true)]
+ public class GetLineups : IReturn>
+ {
+ [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "Type", Description = "Provider Type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Type { get; set; }
+
+ [ApiMember(Name = "Location", Description = "Location", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Location { get; set; }
+
+ [ApiMember(Name = "Country", Description = "Country", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Country { get; set; }
+ }
+
+ [Route("/LiveTv/ListingProviders/SchedulesDirect/Countries", "GET", Summary = "Gets available lineups")]
+ [Authenticated(AllowBeforeStartupWizard = true)]
+ public class GetSchedulesDirectCountries
+ {
+ }
+
+ [Route("/LiveTv/Registration", "GET")]
+ [Authenticated]
+ public class GetLiveTvRegistrationInfo : IReturn
+ {
+ [ApiMember(Name = "ChannelId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ChannelId { get; set; }
+
+ [ApiMember(Name = "ProgramId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ProgramId { get; set; }
+
+ [ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Feature { get; set; }
+ }
+
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager;
+ private readonly IConfigurationManager _config;
+ private readonly IHttpClient _httpClient;
- public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager)
+ public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
+ _config = config;
+ _httpClient = httpClient;
+ }
+
+ public async Task
+ ///
+ /// true if this MediaBrowser.Api.Reports.BaseReportRequest has query limit, false if not.
+ [ApiMember(Name = "HasQueryLimit", Description = "Optional. If specified, results will include all records.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool HasQueryLimit { get; set; }
///
- /// Gets or sets the max offical rating.
- ///
- /// The max offical rating.
- [ApiMember(Name = "MaxOfficialRating", Description = "Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string MaxOfficialRating { get; set; }
-
- [ApiMember(Name = "HasThemeSong", Description = "Optional filter by items with theme songs.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasThemeSong { get; set; }
-
- [ApiMember(Name = "HasThemeVideo", Description = "Optional filter by items with theme videos.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasThemeVideo { get; set; }
-
- [ApiMember(Name = "HasSubtitles", Description = "Optional filter by items with subtitles.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasSubtitles { get; set; }
-
- [ApiMember(Name = "HasSpecialFeature", Description = "Optional filter by items with special features.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasSpecialFeature { get; set; }
-
- [ApiMember(Name = "HasTrailer", Description = "Optional filter by items with trailers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasTrailer { get; set; }
-
- [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string AdjacentTo { get; set; }
-
- [ApiMember(Name = "MinIndexNumber", Description = "Optional filter by minimum index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MinIndexNumber { get; set; }
-
- [ApiMember(Name = "MinPlayers", Description = "Optional filter by minimum number of game players.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MinPlayers { get; set; }
-
- [ApiMember(Name = "MaxPlayers", Description = "Optional filter by maximum number of game players.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxPlayers { get; set; }
-
- [ApiMember(Name = "ParentIndexNumber", Description = "Optional filter by parent index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? ParentIndexNumber { get; set; }
-
- [ApiMember(Name = "HasParentalRating", Description = "Optional filter by items that have or do not have a parental rating", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasParentalRating { get; set; }
+ /// Gets or sets who group this MediaBrowser.Api.Reports.BaseReportRequest.
+ /// Describes who group this MediaBrowser.Api.Reports.BaseReportRequest.
+ [ApiMember(Name = "GroupBy", Description = "Optional. If specified, results will include grouped records.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string GroupBy { get; set; }
- [ApiMember(Name = "IsHD", Description = "Optional filter by items that are HD or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsHD { get; set; }
+ /// Gets or sets the report columns.
+ /// The report columns.
+ [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ReportColumns { get; set; }
- [ApiMember(Name = "LocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string LocationTypes { get; set; }
-
- [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string ExcludeLocationTypes { get; set; }
-
- [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsMissing { get; set; }
-
- [ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsUnaired { get; set; }
-
- [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsVirtualUnaired { get; set; }
-
- [ApiMember(Name = "MinCommunityRating", Description = "Optional filter by minimum community rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public double? MinCommunityRating { get; set; }
-
- [ApiMember(Name = "MinCriticRating", Description = "Optional filter by minimum critic rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public double? MinCriticRating { get; set; }
-
- [ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? AiredDuringSeason { get; set; }
-
- [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string MinPremiereDate { get; set; }
-
- [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string MaxPremiereDate { get; set; }
-
- [ApiMember(Name = "HasOverview", Description = "Optional filter by items that have an overview or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasOverview { get; set; }
-
- [ApiMember(Name = "HasImdbId", Description = "Optional filter by items that have an imdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasImdbId { get; set; }
-
- [ApiMember(Name = "HasTmdbId", Description = "Optional filter by items that have a tmdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasTmdbId { get; set; }
-
- [ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasTvdbId { get; set; }
-
- [ApiMember(Name = "IsYearMismatched", Description = "Optional filter by items that are potentially misidentified.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsYearMismatched { get; set; }
-
- [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsInBoxSet { get; set; }
-
- [ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? IsLocked { get; set; }
-
- [ApiMember(Name = "IsUnidentified", Description = "Optional filter by items that are unidentified by internet metadata providers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? IsUnidentified { get; set; }
-
- [ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? IsPlaceHolder { get; set; }
-
- [ApiMember(Name = "HasOfficialRating", Description = "Optional filter by items that have official ratings", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasOfficialRating { get; set; }
-
- [ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? CollapseBoxSetItems { get; set; }
-
- public string[] GetStudios()
- {
- return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetStudioIds()
- {
- return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetPersonTypes()
- {
- return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetPersonIds()
- {
- return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public VideoType[] GetVideoTypes()
- {
- var val = VideoTypes;
-
- if (string.IsNullOrEmpty(val))
- {
- return new VideoType[] { };
- }
-
- return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray();
- }
+
}
[Route("/Reports/Items", "GET", Summary = "Gets reports based on library items")]
@@ -261,8 +100,27 @@ namespace MediaBrowser.Api.Reports
}
[Route("/Reports/Headers", "GET", Summary = "Gets reports headers based on library items")]
- public class GetReportHeaders : BaseReportRequest, IReturn>
- {
+ public class GetReportHeaders : IReturn>, IReportsHeader
+ {
+ /// Gets or sets the report view.
+ /// The report view.
+ [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ReportView { get; set; }
+
+ /// Gets or sets the report view.
+ /// The report view.
+ [ApiMember(Name = "DisplayType", Description = "The report display type. Values (None, Screen, Export, ScreenExport)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string DisplayType { get; set; }
+
+ /// Gets or sets a list of types of the include items.
+ /// A list of types of the include items.
+ [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; }
+
+ /// Gets or sets the report columns.
+ /// The report columns.
+ [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ReportColumns { get; set; }
}
[Route("/Reports/Statistics", "GET", Summary = "Gets reports statistics based on library items")]
@@ -273,7 +131,7 @@ namespace MediaBrowser.Api.Reports
}
[Route("/Reports/Items/Download", "GET", Summary = "Downloads report")]
- public class GetReportDownload : BaseReportRequest
+ public class GetReportDownload : BaseReportRequest, IReportsDownload
{
public GetReportDownload()
{
@@ -281,6 +139,66 @@ namespace MediaBrowser.Api.Reports
}
public ReportExportType ExportType { get; set; }
+
+ /// Gets or sets the minimum date.
+ /// The minimum date.
+ [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string MinDate { get; set; }
+
}
+
+ [Route("/Reports/Activities", "GET", Summary = "Gets activities entries")]
+ public class GetActivityLogs : IReturn, IReportsQuery, IReportsDownload
+ {
+ /// Gets or sets the report view.
+ /// The report view.
+ [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ReportView { get; set; }
+
+ /// Gets or sets the report view.
+ /// The report view.
+ [ApiMember(Name = "DisplayType", Description = "The report display type. Values (None, Screen, Export, ScreenExport)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string DisplayType { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.GetActivityLogs has
+ /// query limit.
+ ///
+ /// true if this MediaBrowser.Api.Reports.GetActivityLogs has query limit, false if not.
+ [ApiMember(Name = "HasQueryLimit", Description = "Optional. If specified, results will include all records.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool HasQueryLimit { get; set; }
+
+ /// Gets or sets who group this MediaBrowser.Api.Reports.GetActivityLogs.
+ /// Describes who group this MediaBrowser.Api.Reports.GetActivityLogs.
+ [ApiMember(Name = "GroupBy", Description = "Optional. If specified, results will include grouped records.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string GroupBy { get; set; }
+
+ /// Gets or sets the report columns.
+ /// The report columns.
+ [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ReportColumns { get; set; }
+
+ ///
+ /// 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 minimum date.
+ /// The minimum date.
+ [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string MinDate { 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; }
+ }
}
diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs
index ebf5183c51..82e7264f15 100644
--- a/MediaBrowser.Api/Reports/ReportsService.cs
+++ b/MediaBrowser.Api/Reports/ReportsService.cs
@@ -24,1139 +24,666 @@ using System.Text;
namespace MediaBrowser.Api.Reports
{
- /// The reports service.
- ///
- public class ReportsService : BaseApiService
- {
-
-
- /// Manager for user.
- private readonly IUserManager _userManager;
-
- /// Manager for library.
- private readonly ILibraryManager _libraryManager;
- /// The localization.
- private readonly ILocalizationManager _localization;
-
- ///
- /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class.
- /// Manager for user.
- /// Manager for library.
- /// The localization.
- public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization)
- {
- _userManager = userManager;
- _libraryManager = libraryManager;
- _localization = localization;
- }
-
- /// Gets the given request.
- /// The request.
- /// A Task<object>
- public async Task Get(GetReportHeaders request)
- {
- if (string.IsNullOrEmpty(request.IncludeItemTypes))
- return null;
-
- ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
- ReportBuilder reportBuilder = new ReportBuilder(_libraryManager);
- var reportResult = reportBuilder.GetReportHeaders(reportRowType, request);
-
- return ToOptimizedResult(reportResult);
-
- }
-
- /// Gets the given request.
- /// The request.
- /// A Task<object>
- public async Task Get(GetItemReport request)
- {
- if (string.IsNullOrEmpty(request.IncludeItemTypes))
- return null;
-
- var reportResult = await GetReportResult(request);
-
- return ToOptimizedResult(reportResult);
- }
-
- /// Gets the given request.
- /// The request.
- /// A Task<object>
- public async Task Get(GetReportDownload request)
- {
- if (string.IsNullOrEmpty(request.IncludeItemTypes))
- return null;
-
- var headers = new Dictionary();
- string fileExtension = "csv";
- string contentType = "text/plain;charset='utf-8'";
-
- switch (request.ExportType)
- {
- case ReportExportType.CSV:
- break;
- case ReportExportType.Excel:
- contentType = "application/vnd.ms-excel";
- fileExtension = "xls";
- break;
- }
-
- var filename = "ReportExport." + fileExtension;
- headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename);
- headers["Content-Encoding"] = "UTF-8";
-
- ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
- ReportBuilder reportBuilder = new ReportBuilder(_libraryManager);
- QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false);
- ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request);
-
- reportResult.TotalRecordCount = queryResult.TotalRecordCount;
-
- string result = string.Empty;
- switch (request.ExportType)
- {
- case ReportExportType.CSV:
- result = new ReportExport().ExportToCsv(reportResult);
- break;
- case ReportExportType.Excel:
- result = new ReportExport().ExportToExcel(reportResult);
- break;
- }
-
- object ro = ResultFactory.GetResult(result, contentType, headers);
- return ro;
- }
-
- /// Gets the given request.
- /// The request.
- /// A Task<object>
- public async Task Get(GetReportStatistics request)
- {
- if (string.IsNullOrEmpty(request.IncludeItemTypes))
- return null;
- var reportResult = await GetReportStatistic(request);
-
- return ToOptimizedResult(reportResult);
- }
-
- /// Gets report statistic.
- /// The request.
- /// The report statistic.
- private async Task GetReportStatistic(GetReportStatistics request)
- {
- ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
- QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false);
-
- ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager);
- ReportStatResult reportResult = reportBuilder.GetReportStatResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5);
- reportResult.TotalRecordCount = reportResult.Groups.Count();
- return reportResult;
- }
-
- /// Gets report result.
- /// The request.
- /// The report result.
- private async Task GetReportResult(GetItemReport request)
- {
-
- ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
- ReportBuilder reportBuilder = new ReportBuilder(_libraryManager);
- QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false);
- ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request);
- reportResult.TotalRecordCount = queryResult.TotalRecordCount;
-
- return reportResult;
- }
-
- /// Gets query result.
- /// The request.
- /// The query result.
- private async Task> GetQueryResult(BaseReportRequest request)
- {
- // Placeholder in case needed later
- request.Recursive = true;
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
- request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts";
-
- var parentItem = string.IsNullOrEmpty(request.ParentId) ?
- (user == null ? _libraryManager.RootFolder : user.RootFolder) :
- _libraryManager.GetItemById(request.ParentId);
-
- var item = string.IsNullOrEmpty(request.ParentId) ?
- user == null ? _libraryManager.RootFolder : user.RootFolder :
- parentItem;
-
- IEnumerable items;
-
- if (request.Recursive)
- {
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
- return result;
- }
- else
- {
- if (user == null)
- {
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
- return result;
- }
-
- var userRoot = item as UserRootFolder;
-
- if (userRoot == null)
- {
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
-
- return result;
- }
-
- items = ((Folder)item).GetChildren(user, true);
- }
-
- return new QueryResult { Items = items.ToArray() };
-
- }
-
- /// Gets items query.
- /// The request.
- /// The user.
- /// The items query.
- private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user)
- {
- var query = new InternalItemsQuery
- {
- User = user,
- IsPlayed = request.IsPlayed,
- MediaTypes = request.GetMediaTypes(),
- IncludeItemTypes = request.GetIncludeItemTypes(),
- ExcludeItemTypes = request.GetExcludeItemTypes(),
- Recursive = true,
- SortBy = request.GetOrderBy(),
- SortOrder = request.SortOrder ?? SortOrder.Ascending,
-
- Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager),
- StartIndex = request.StartIndex,
- IsMissing = request.IsMissing,
- IsVirtualUnaired = request.IsVirtualUnaired,
- IsUnaired = request.IsUnaired,
- CollapseBoxSetItems = request.CollapseBoxSetItems,
- NameLessThan = request.NameLessThan,
- NameStartsWith = request.NameStartsWith,
- NameStartsWithOrGreater = request.NameStartsWithOrGreater,
- HasImdbId = request.HasImdbId,
- IsYearMismatched = request.IsYearMismatched,
- IsUnidentified = request.IsUnidentified,
- IsPlaceHolder = request.IsPlaceHolder,
- IsLocked = request.IsLocked,
- IsInBoxSet = request.IsInBoxSet,
- IsHD = request.IsHD,
- Is3D = request.Is3D,
- HasTvdbId = request.HasTvdbId,
- HasTmdbId = request.HasTmdbId,
- HasOverview = request.HasOverview,
- HasOfficialRating = request.HasOfficialRating,
- HasParentalRating = request.HasParentalRating,
- HasSpecialFeature = request.HasSpecialFeature,
- HasSubtitles = request.HasSubtitles,
- HasThemeSong = request.HasThemeSong,
- HasThemeVideo = request.HasThemeVideo,
- HasTrailer = request.HasTrailer,
- Tags = request.GetTags(),
- OfficialRatings = request.GetOfficialRatings(),
- Genres = request.GetGenres(),
- Studios = request.GetStudios(),
- StudioIds = request.GetStudioIds(),
- Person = request.Person,
- PersonIds = request.GetPersonIds(),
- PersonTypes = request.GetPersonTypes(),
- Years = request.GetYears(),
- ImageTypes = request.GetImageTypes().ToArray(),
- VideoTypes = request.GetVideoTypes().ToArray(),
- AdjacentTo = request.AdjacentTo
- };
-
- if (!string.IsNullOrWhiteSpace(request.Ids))
- {
- query.CollapseBoxSetItems = false;
- }
-
- foreach (var filter in request.GetFilters())
- {
- switch (filter)
- {
- case ItemFilter.Dislikes:
- query.IsLiked = false;
- break;
- case ItemFilter.IsFavorite:
- query.IsFavorite = true;
- break;
- case ItemFilter.IsFavoriteOrLikes:
- query.IsFavoriteOrLiked = true;
- break;
- case ItemFilter.IsFolder:
- query.IsFolder = true;
- break;
- case ItemFilter.IsNotFolder:
- query.IsFolder = false;
- break;
- case ItemFilter.IsPlayed:
- query.IsPlayed = true;
- break;
- case ItemFilter.IsRecentlyAdded:
- break;
- case ItemFilter.IsResumable:
- query.IsResumable = true;
- break;
- case ItemFilter.IsUnplayed:
- query.IsPlayed = false;
- break;
- case ItemFilter.Likes:
- query.IsLiked = true;
- break;
- }
- }
-
- if (request.HasQueryLimit)
- query.Limit = request.Limit;
- return query;
- }
-
- /// Applies filtering.
- /// The items.
- /// The filter.
- /// The user.
- /// The repository.
- /// IEnumerable{BaseItem}.
- internal static IEnumerable ApplyFilter(IEnumerable items, ItemFilter filter, User user, IUserDataManager repository)
- {
- // Avoid implicitly captured closure
- var currentUser = user;
-
- switch (filter)
- {
- case ItemFilter.IsFavoriteOrLikes:
- return items.Where(item =>
- {
- var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
- if (userdata == null)
- {
- return false;
- }
-
- var likes = userdata.Likes ?? false;
- var favorite = userdata.IsFavorite;
-
- return likes || favorite;
- });
-
- case ItemFilter.Likes:
- return items.Where(item =>
- {
- var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
- return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
- });
-
- case ItemFilter.Dislikes:
- return items.Where(item =>
- {
- var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
- return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
- });
-
- case ItemFilter.IsFavorite:
- return items.Where(item =>
- {
- var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
- return userdata != null && userdata.IsFavorite;
- });
-
- case ItemFilter.IsResumable:
- return items.Where(item =>
- {
- var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
- return userdata != null && userdata.PlaybackPositionTicks > 0;
- });
-
- case ItemFilter.IsPlayed:
- return items.Where(item => item.IsPlayed(currentUser));
-
- case ItemFilter.IsUnplayed:
- return items.Where(item => item.IsUnplayed(currentUser));
-
- case ItemFilter.IsFolder:
- return items.Where(item => item.IsFolder);
-
- case ItemFilter.IsNotFolder:
- return items.Where(item => !item.IsFolder);
-
- case ItemFilter.IsRecentlyAdded:
- return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10);
- }
-
- return items;
- }
-
- /// Applies the additional filters.
- /// The request.
- /// Zero-based index of the.
- /// The user.
- /// true if this object is pre filtered.
- /// Manager for library.
- /// true if it succeeds, false if it fails.
- private bool ApplyAdditionalFilters(BaseReportRequest request, BaseItem i, User user, bool isPreFiltered, ILibraryManager libraryManager)
- {
- var video = i as Video;
-
- if (!isPreFiltered)
- {
- var mediaTypes = request.GetMediaTypes();
- if (mediaTypes.Length > 0)
- {
- if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- if (request.IsPlayed.HasValue)
- {
- var val = request.IsPlayed.Value;
- if (i.IsPlayed(user) != val)
- {
- return false;
- }
- }
-
- // Exclude item types
- var excluteItemTypes = request.GetExcludeItemTypes();
- if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- // Include item types
- var includeItemTypes = request.GetIncludeItemTypes();
- if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (request.IsInBoxSet.HasValue)
- {
- var val = request.IsInBoxSet.Value;
- if (i.Parents.OfType().Any() != val)
- {
- return false;
- }
- }
-
- // Filter by Video3DFormat
- if (request.Is3D.HasValue)
- {
- var val = request.Is3D.Value;
-
- if (video == null || val != video.Video3DFormat.HasValue)
- {
- return false;
- }
- }
-
- if (request.IsHD.HasValue)
- {
- var val = request.IsHD.Value;
-
- if (video == null || val != video.IsHD)
- {
- return false;
- }
- }
-
- if (request.IsUnidentified.HasValue)
- {
- var val = request.IsUnidentified.Value;
- if (i.IsUnidentified != val)
- {
- return false;
- }
- }
-
- if (request.IsLocked.HasValue)
- {
- var val = request.IsLocked.Value;
- if (i.IsLocked != val)
- {
- return false;
- }
- }
-
- if (request.HasOverview.HasValue)
- {
- var filterValue = request.HasOverview.Value;
-
- var hasValue = !string.IsNullOrEmpty(i.Overview);
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (request.HasImdbId.HasValue)
- {
- var filterValue = request.HasImdbId.Value;
-
- var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb));
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (request.HasTmdbId.HasValue)
- {
- var filterValue = request.HasTmdbId.Value;
-
- var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb));
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (request.HasTvdbId.HasValue)
- {
- var filterValue = request.HasTvdbId.Value;
-
- var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb));
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (request.IsYearMismatched.HasValue)
- {
- var filterValue = request.IsYearMismatched.Value;
-
- if (UserViewBuilder.IsYearMismatched(i, libraryManager) != filterValue)
- {
- return false;
- }
- }
-
- if (request.HasOfficialRating.HasValue)
- {
- var filterValue = request.HasOfficialRating.Value;
-
- var hasValue = !string.IsNullOrEmpty(i.OfficialRating);
-
- if (hasValue != filterValue)
- {
- return false;
- }
- }
-
- if (request.IsPlaceHolder.HasValue)
- {
- var filterValue = request.IsPlaceHolder.Value;
-
- var isPlaceHolder = false;
-
- var hasPlaceHolder = i as ISupportsPlaceHolders;
-
- if (hasPlaceHolder != null)
- {
- isPlaceHolder = hasPlaceHolder.IsPlaceHolder;
- }
-
- if (isPlaceHolder != filterValue)
- {
- return false;
- }
- }
-
- if (request.HasSpecialFeature.HasValue)
- {
- var filterValue = request.HasSpecialFeature.Value;
-
- var movie = i as IHasSpecialFeatures;
-
- if (movie != null)
- {
- var ok = filterValue
- ? movie.SpecialFeatureIds.Count > 0
- : movie.SpecialFeatureIds.Count == 0;
-
- if (!ok)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (request.HasSubtitles.HasValue)
- {
- var val = request.HasSubtitles.Value;
-
- if (video == null || val != video.HasSubtitles)
- {
- return false;
- }
- }
-
- if (request.HasParentalRating.HasValue)
- {
- var val = request.HasParentalRating.Value;
-
- var rating = i.CustomRating;
-
- if (string.IsNullOrEmpty(rating))
- {
- rating = i.OfficialRating;
- }
-
- if (val)
- {
- if (string.IsNullOrEmpty(rating))
- {
- return false;
- }
- }
- else
- {
- if (!string.IsNullOrEmpty(rating))
- {
- return false;
- }
- }
- }
-
- if (request.HasTrailer.HasValue)
- {
- var val = request.HasTrailer.Value;
- var trailerCount = 0;
-
- var hasTrailers = i as IHasTrailers;
- if (hasTrailers != null)
- {
- trailerCount = hasTrailers.GetTrailerIds().Count;
- }
-
- var ok = val ? trailerCount > 0 : trailerCount == 0;
-
- if (!ok)
- {
- return false;
- }
- }
-
- if (request.HasThemeSong.HasValue)
- {
- var filterValue = request.HasThemeSong.Value;
-
- var themeCount = 0;
- var iHasThemeMedia = i as IHasThemeMedia;
-
- if (iHasThemeMedia != null)
- {
- themeCount = iHasThemeMedia.ThemeSongIds.Count;
- }
- var ok = filterValue ? themeCount > 0 : themeCount == 0;
-
- if (!ok)
- {
- return false;
- }
- }
-
- if (request.HasThemeVideo.HasValue)
- {
- var filterValue = request.HasThemeVideo.Value;
-
- var themeCount = 0;
- var iHasThemeMedia = i as IHasThemeMedia;
-
- if (iHasThemeMedia != null)
- {
- themeCount = iHasThemeMedia.ThemeVideoIds.Count;
- }
- var ok = filterValue ? themeCount > 0 : themeCount == 0;
-
- if (!ok)
- {
- return false;
- }
- }
-
- // Apply tag filter
- var tags = request.GetTags();
- if (tags.Length > 0)
- {
- var hasTags = i as IHasTags;
- if (hasTags == null)
- {
- return false;
- }
- if (!(tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))))
- {
- return false;
- }
- }
-
- // Apply official rating filter
- var officialRatings = request.GetOfficialRatings();
- if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty))
- {
- return false;
- }
-
- // Apply genre filter
- var genres = request.GetGenres();
- if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase))))
- {
- return false;
- }
-
- // Filter by VideoType
- var videoTypes = request.GetVideoTypes();
- if (videoTypes.Length > 0 && (video == null || !videoTypes.Contains(video.VideoType)))
- {
- return false;
- }
-
- var imageTypes = request.GetImageTypes().ToList();
- if (imageTypes.Count > 0)
- {
- if (!(imageTypes.Any(i.HasImage)))
- {
- return false;
- }
- }
-
- // Apply studio filter
- var studios = request.GetStudios();
- if (studios.Length > 0 && !studios.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase)))
- {
- return false;
- }
-
- // Apply studio filter
- var studioIds = request.GetStudioIds();
- if (studioIds.Length > 0 && !studioIds.Any(id =>
- {
- var studioItem = libraryManager.GetItemById(id);
- return studioItem != null && i.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase);
- }))
- {
- return false;
- }
-
- // Apply year filter
- var years = request.GetYears();
- if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value)))
- {
- return false;
- }
-
- // Apply person filter
- var personIds = request.GetPersonIds();
- if (personIds.Length > 0)
- {
- var names = personIds
- .Select(libraryManager.GetItemById)
- .Select(p => p == null ? "-1" : p.Name)
- .ToList();
-
- if (!(names.Any(v => _libraryManager.GetPeople(i).Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase))))
- {
- return false;
- }
- }
-
- // Apply person filter
- if (!string.IsNullOrEmpty(request.Person))
- {
- var personTypes = request.GetPersonTypes();
-
- if (personTypes.Length == 0)
- {
- if (!(_libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase))))
- {
- return false;
- }
- }
- else
- {
- var types = personTypes;
-
- var ok = new[] { i }.Any(item =>
- _libraryManager.GetPeople(i).Any(p =>
- p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase))));
-
- if (!ok)
- {
- return false;
- }
- }
- }
- }
-
- if (request.MinCommunityRating.HasValue)
- {
- var val = request.MinCommunityRating.Value;
-
- if (!(i.CommunityRating.HasValue && i.CommunityRating >= val))
- {
- return false;
- }
- }
-
- if (request.MinCriticRating.HasValue)
- {
- var val = request.MinCriticRating.Value;
-
- var hasCriticRating = i as IHasCriticRating;
-
- if (hasCriticRating != null)
- {
- if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val))
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- // Artists
- if (!string.IsNullOrEmpty(request.ArtistIds))
- {
- var artistIds = request.ArtistIds.Split('|');
-
- var audio = i as IHasArtist;
-
- if (!(audio != null && artistIds.Any(id =>
- {
- var artistItem = libraryManager.GetItemById(id);
- return artistItem != null && audio.HasAnyArtist(artistItem.Name);
- })))
- {
- return false;
- }
- }
-
- // Artists
- if (!string.IsNullOrEmpty(request.Artists))
- {
- var artists = request.Artists.Split('|');
-
- var audio = i as IHasArtist;
-
- if (!(audio != null && artists.Any(audio.HasAnyArtist)))
- {
- return false;
- }
- }
-
- // Albums
- if (!string.IsNullOrEmpty(request.Albums))
- {
- var albums = request.Albums.Split('|');
-
- var audio = i as Audio;
-
- if (audio != null)
- {
- if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- var album = i as MusicAlbum;
-
- if (album != null)
- {
- if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- var musicVideo = i as MusicVideo;
-
- if (musicVideo != null)
- {
- if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- return false;
- }
-
- // Min index number
- if (request.MinIndexNumber.HasValue)
- {
- if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value))
- {
- return false;
- }
- }
-
- // Min official rating
- if (!string.IsNullOrEmpty(request.MinOfficialRating))
- {
- var level = _localization.GetRatingLevel(request.MinOfficialRating);
-
- if (level.HasValue)
- {
- var rating = i.CustomRating;
-
- if (string.IsNullOrEmpty(rating))
- {
- rating = i.OfficialRating;
- }
-
- if (!string.IsNullOrEmpty(rating))
- {
- var itemLevel = _localization.GetRatingLevel(rating);
-
- if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value))
- {
- return false;
- }
- }
- }
- }
-
- // Max official rating
- if (!string.IsNullOrEmpty(request.MaxOfficialRating))
- {
- var level = _localization.GetRatingLevel(request.MaxOfficialRating);
-
- if (level.HasValue)
- {
- var rating = i.CustomRating;
-
- if (string.IsNullOrEmpty(rating))
- {
- rating = i.OfficialRating;
- }
-
- if (!string.IsNullOrEmpty(rating))
- {
- var itemLevel = _localization.GetRatingLevel(rating);
-
- if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value))
- {
- return false;
- }
- }
- }
- }
-
- // LocationTypes
- if (!string.IsNullOrEmpty(request.LocationTypes))
- {
- var vals = request.LocationTypes.Split(',');
- if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- // ExcludeLocationTypes
- if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
- {
- var vals = request.ExcludeLocationTypes.Split(',');
- if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
- {
- var ok = new[] { i }.OfType()
- .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1);
-
- if (!ok)
- {
- return false;
- }
- }
-
- // Filter by Series Status
- if (!string.IsNullOrEmpty(request.SeriesStatus))
- {
- var vals = request.SeriesStatus.Split(',');
-
- var ok = new[] { i }.OfType().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase));
-
- if (!ok)
- {
- return false;
- }
- }
-
- // Filter by Series AirDays
- if (!string.IsNullOrEmpty(request.AirDays))
- {
- var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true));
-
- var ok = new[] { i }.OfType().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d)));
-
- if (!ok)
- {
- return false;
- }
- }
-
- if (request.MinPlayers.HasValue)
- {
- var filterValue = request.MinPlayers.Value;
-
- var game = i as Game;
-
- if (game != null)
- {
- var players = game.PlayersSupported ?? 1;
-
- var ok = players >= filterValue;
-
- if (!ok)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (request.MaxPlayers.HasValue)
- {
- var filterValue = request.MaxPlayers.Value;
-
- var game = i as Game;
-
- if (game != null)
- {
- var players = game.PlayersSupported ?? 1;
-
- var ok = players <= filterValue;
-
- if (!ok)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (request.ParentIndexNumber.HasValue)
- {
- var filterValue = request.ParentIndexNumber.Value;
-
- var episode = i as Episode;
-
- if (episode != null)
- {
- if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue)
- {
- return false;
- }
- }
-
- var song = i as Audio;
-
- if (song != null)
- {
- if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue)
- {
- return false;
- }
- }
- }
-
- if (request.AiredDuringSeason.HasValue)
- {
- var episode = i as Episode;
-
- if (episode == null)
- {
- return false;
- }
-
- if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any())
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(request.MinPremiereDate))
- {
- var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
-
- if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date))
- {
- return false;
- }
- }
-
- if (!string.IsNullOrEmpty(request.MaxPremiereDate))
- {
- var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
-
- if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date))
- {
- return false;
- }
- }
-
- return true;
- }
-
- /// Applies the paging.
- /// The request.
- /// The items.
- /// IEnumerable{BaseItem}.
- private IEnumerable ApplyPaging(GetItems request, IEnumerable items)
- {
- // Start at
- if (request.StartIndex.HasValue)
- {
- items = items.Skip(request.StartIndex.Value);
- }
-
- // Return limit
- if (request.Limit.HasValue)
- {
- items = items.Take(request.Limit.Value);
- }
-
- return items;
- }
-
- }
+ /// The reports service.
+ ///
+ public class ReportsService : BaseApiService
+ {
+ #region [Constructors]
+
+ ///
+ /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class.
+ /// Manager for user.
+ /// Manager for library.
+ /// The localization.
+ /// Manager for activity.
+ public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IActivityManager activityManager, IActivityRepository repo)
+ {
+ _userManager = userManager;
+ _libraryManager = libraryManager;
+ _localization = localization;
+ _activityManager = activityManager;
+ _repo = repo;
+ }
+
+ #endregion
+
+ #region [Private Fields]
+
+ private readonly IActivityManager _activityManager; ///< Manager for activity
+
+ /// Manager for library.
+ private readonly ILibraryManager _libraryManager; ///< Manager for library
+ /// The localization.
+
+ private readonly ILocalizationManager _localization; ///< The localization
+
+ private readonly IActivityRepository _repo;
+
+ /// Manager for user.
+ private readonly IUserManager _userManager; ///< Manager for user
+
+ #endregion
+
+ #region [Public Methods]
+
+ /// Gets the given request.
+ /// The request.
+ /// A Task<object>
+ public async Task Get(GetActivityLogs request)
+ {
+ request.DisplayType = "Screen";
+ ReportResult result = await GetReportActivities(request).ConfigureAwait(false);
+ return ToOptimizedResult(result);
+ }
+
+ /// Gets the given request.
+ /// The request.
+ /// A Task<object>
+ public async Task Get(GetReportHeaders request)
+ {
+ if (string.IsNullOrEmpty(request.IncludeItemTypes))
+ return null;
+
+ request.DisplayType = "Screen";
+ ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView);
+
+ List result = new List();
+ switch (reportViewType)
+ {
+ case ReportViewType.ReportData:
+ ReportBuilder dataBuilder = new ReportBuilder(_libraryManager);
+ result = dataBuilder.GetHeaders(request);
+ break;
+ case ReportViewType.ReportStatistics:
+ break;
+ case ReportViewType.ReportActivities:
+ ReportActivitiesBuilder activityBuilder = new ReportActivitiesBuilder(_libraryManager, _userManager);
+ result = activityBuilder.GetHeaders(request);
+ break;
+ }
+
+ return ToOptimizedResult(result);
+
+ }
+
+ /// Gets the given request.
+ /// The request.
+ /// A Task<object>
+ public async Task Get(GetItemReport request)
+ {
+ if (string.IsNullOrEmpty(request.IncludeItemTypes))
+ return null;
+
+ request.DisplayType = "Screen";
+ var reportResult = await GetReportResult(request);
+
+ return ToOptimizedResult(reportResult);
+ }
+
+ /// Gets the given request.
+ /// The request.
+ /// A Task<object>
+ public async Task Get(GetReportStatistics request)
+ {
+ if (string.IsNullOrEmpty(request.IncludeItemTypes))
+ return null;
+ request.DisplayType = "Screen";
+ var reportResult = await GetReportStatistic(request);
+
+ return ToOptimizedResult(reportResult);
+ }
+
+ /// Gets the given request.
+ /// The request.
+ /// A Task<object>
+ public async Task Get(GetReportDownload request)
+ {
+ if (string.IsNullOrEmpty(request.IncludeItemTypes))
+ return null;
+
+ request.DisplayType = "Export";
+ ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView);
+ var headers = new Dictionary();
+ string fileExtension = "csv";
+ string contentType = "text/plain;charset='utf-8'";
+
+ switch (request.ExportType)
+ {
+ case ReportExportType.CSV:
+ break;
+ case ReportExportType.Excel:
+ contentType = "application/vnd.ms-excel";
+ fileExtension = "xls";
+ break;
+ }
+
+ var filename = "ReportExport." + fileExtension;
+ headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename);
+ headers["Content-Encoding"] = "UTF-8";
+
+ ReportResult result = null;
+ switch (reportViewType)
+ {
+ case ReportViewType.ReportStatistics:
+ case ReportViewType.ReportData:
+ ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
+ ReportBuilder dataBuilder = new ReportBuilder(_libraryManager);
+ QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false);
+ result = dataBuilder.GetResult(queryResult.Items, request);
+ result.TotalRecordCount = queryResult.TotalRecordCount;
+ break;
+ case ReportViewType.ReportActivities:
+ result = await GetReportActivities(request).ConfigureAwait(false);
+ break;
+ }
+
+ string returnResult = string.Empty;
+ switch (request.ExportType)
+ {
+ case ReportExportType.CSV:
+ returnResult = new ReportExport().ExportToCsv(result);
+ break;
+ case ReportExportType.Excel:
+ returnResult = new ReportExport().ExportToExcel(result);
+ break;
+ }
+
+ object ro = ResultFactory.GetResult(returnResult, contentType, headers);
+ return ro;
+ }
+
+ #endregion
+
+ #region [Private Methods]
+
+ /// Gets items query.
+ /// The request.
+ /// The user.
+ /// The items query.
+ private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user)
+ {
+ var query = new InternalItemsQuery
+ {
+ User = user,
+ IsPlayed = request.IsPlayed,
+ MediaTypes = request.GetMediaTypes(),
+ IncludeItemTypes = request.GetIncludeItemTypes(),
+ ExcludeItemTypes = request.GetExcludeItemTypes(),
+ Recursive = request.Recursive,
+ SortBy = request.GetOrderBy(),
+ SortOrder = request.SortOrder ?? SortOrder.Ascending,
+
+ Filter = i => ApplyAdditionalFilters(request, i, user, _libraryManager),
+
+ Limit = request.Limit,
+ StartIndex = request.StartIndex,
+ IsMissing = request.IsMissing,
+ IsVirtualUnaired = request.IsVirtualUnaired,
+ IsUnaired = request.IsUnaired,
+ CollapseBoxSetItems = request.CollapseBoxSetItems,
+ NameLessThan = request.NameLessThan,
+ NameStartsWith = request.NameStartsWith,
+ NameStartsWithOrGreater = request.NameStartsWithOrGreater,
+ HasImdbId = request.HasImdbId,
+ IsYearMismatched = request.IsYearMismatched,
+ IsUnidentified = request.IsUnidentified,
+ IsPlaceHolder = request.IsPlaceHolder,
+ IsLocked = request.IsLocked,
+ IsInBoxSet = request.IsInBoxSet,
+ IsHD = request.IsHD,
+ Is3D = request.Is3D,
+ HasTvdbId = request.HasTvdbId,
+ HasTmdbId = request.HasTmdbId,
+ HasOverview = request.HasOverview,
+ HasOfficialRating = request.HasOfficialRating,
+ HasParentalRating = request.HasParentalRating,
+ HasSpecialFeature = request.HasSpecialFeature,
+ HasSubtitles = request.HasSubtitles,
+ HasThemeSong = request.HasThemeSong,
+ HasThemeVideo = request.HasThemeVideo,
+ HasTrailer = request.HasTrailer,
+ Tags = request.GetTags(),
+ OfficialRatings = request.GetOfficialRatings(),
+ Genres = request.GetGenres(),
+ Studios = request.GetStudios(),
+ StudioIds = request.GetStudioIds(),
+ Person = request.Person,
+ PersonIds = request.GetPersonIds(),
+ PersonTypes = request.GetPersonTypes(),
+ Years = request.GetYears(),
+ ImageTypes = request.GetImageTypes().ToArray(),
+ VideoTypes = request.GetVideoTypes().ToArray(),
+ AdjacentTo = request.AdjacentTo,
+ ItemIds = request.GetItemIds(),
+ MinPlayers = request.MinPlayers,
+ MaxPlayers = request.MaxPlayers,
+ MinCommunityRating = request.MinCommunityRating,
+ MinCriticRating = request.MinCriticRating
+ };
+
+ if (!string.IsNullOrWhiteSpace(request.Ids))
+ {
+ query.CollapseBoxSetItems = false;
+ }
+
+ foreach (var filter in request.GetFilters())
+ {
+ switch (filter)
+ {
+ case ItemFilter.Dislikes:
+ query.IsLiked = false;
+ break;
+ case ItemFilter.IsFavorite:
+ query.IsFavorite = true;
+ break;
+ case ItemFilter.IsFavoriteOrLikes:
+ query.IsFavoriteOrLiked = true;
+ break;
+ case ItemFilter.IsFolder:
+ query.IsFolder = true;
+ break;
+ case ItemFilter.IsNotFolder:
+ query.IsFolder = false;
+ break;
+ case ItemFilter.IsPlayed:
+ query.IsPlayed = true;
+ break;
+ case ItemFilter.IsRecentlyAdded:
+ break;
+ case ItemFilter.IsResumable:
+ query.IsResumable = true;
+ break;
+ case ItemFilter.IsUnplayed:
+ query.IsPlayed = false;
+ break;
+ case ItemFilter.Likes:
+ query.IsLiked = true;
+ break;
+ }
+ }
+
+ if (request.HasQueryLimit)
+ query.Limit = request.Limit;
+
+ return query;
+ }
+
+ private bool ApplyAdditionalFilters(BaseReportRequest request, BaseItem i, User user, ILibraryManager libraryManager)
+ {
+ // Artists
+ if (!string.IsNullOrEmpty(request.ArtistIds))
+ {
+ var artistIds = request.ArtistIds.Split(new[] { '|', ',' });
+
+ var audio = i as IHasArtist;
+
+ if (!(audio != null && artistIds.Any(id =>
+ {
+ var artistItem = libraryManager.GetItemById(id);
+ return artistItem != null && audio.HasAnyArtist(artistItem.Name);
+ })))
+ {
+ return false;
+ }
+ }
+
+ // Artists
+ if (!string.IsNullOrEmpty(request.Artists))
+ {
+ var artists = request.Artists.Split('|');
+
+ var audio = i as IHasArtist;
+
+ if (!(audio != null && artists.Any(audio.HasAnyArtist)))
+ {
+ return false;
+ }
+ }
+
+ // Albums
+ if (!string.IsNullOrEmpty(request.Albums))
+ {
+ var albums = request.Albums.Split('|');
+
+ var audio = i as Audio;
+
+ if (audio != null)
+ {
+ if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+ }
+
+ var album = i as MusicAlbum;
+
+ if (album != null)
+ {
+ if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+ }
+
+ var musicVideo = i as MusicVideo;
+
+ if (musicVideo != null)
+ {
+ if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ // Min index number
+ if (request.MinIndexNumber.HasValue)
+ {
+ if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value))
+ {
+ return false;
+ }
+ }
+
+ // Min official rating
+ if (!string.IsNullOrEmpty(request.MinOfficialRating))
+ {
+ var level = _localization.GetRatingLevel(request.MinOfficialRating);
+
+ if (level.HasValue)
+ {
+ var rating = i.CustomRating;
+
+ if (string.IsNullOrEmpty(rating))
+ {
+ rating = i.OfficialRating;
+ }
+
+ if (!string.IsNullOrEmpty(rating))
+ {
+ var itemLevel = _localization.GetRatingLevel(rating);
+
+ if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value))
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ // Max official rating
+ if (!string.IsNullOrEmpty(request.MaxOfficialRating))
+ {
+ var level = _localization.GetRatingLevel(request.MaxOfficialRating);
+
+ if (level.HasValue)
+ {
+ var rating = i.CustomRating;
+
+ if (string.IsNullOrEmpty(rating))
+ {
+ rating = i.OfficialRating;
+ }
+
+ if (!string.IsNullOrEmpty(rating))
+ {
+ var itemLevel = _localization.GetRatingLevel(rating);
+
+ if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value))
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ // LocationTypes
+ if (!string.IsNullOrEmpty(request.LocationTypes))
+ {
+ var vals = request.LocationTypes.Split(',');
+ if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ // ExcludeLocationTypes
+ if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
+ {
+ var vals = request.ExcludeLocationTypes.Split(',');
+ if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
+ {
+ var ok = new[] { i }.OfType()
+ .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1);
+
+ if (!ok)
+ {
+ return false;
+ }
+ }
+
+ // Filter by Series Status
+ if (!string.IsNullOrEmpty(request.SeriesStatus))
+ {
+ var vals = request.SeriesStatus.Split(',');
+
+ var ok = new[] { i }.OfType().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase));
+
+ if (!ok)
+ {
+ return false;
+ }
+ }
+
+ // Filter by Series AirDays
+ if (!string.IsNullOrEmpty(request.AirDays))
+ {
+ var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true));
+
+ var ok = new[] { i }.OfType().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d)));
+
+ if (!ok)
+ {
+ return false;
+ }
+ }
+
+ if (request.ParentIndexNumber.HasValue)
+ {
+ var filterValue = request.ParentIndexNumber.Value;
+
+ var episode = i as Episode;
+
+ if (episode != null)
+ {
+ if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue)
+ {
+ return false;
+ }
+ }
+
+ var song = i as Audio;
+
+ if (song != null)
+ {
+ if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (request.AiredDuringSeason.HasValue)
+ {
+ var episode = i as Episode;
+
+ if (episode == null)
+ {
+ return false;
+ }
+
+ if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any())
+ {
+ return false;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(request.MinPremiereDate))
+ {
+ var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
+
+ if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date))
+ {
+ return false;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(request.MaxPremiereDate))
+ {
+ var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
+
+ if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /// Applies the paging.
+ /// The request.
+ /// The items.
+ /// IEnumerable{BaseItem}.
+ private IEnumerable ApplyPaging(BaseReportRequest request, IEnumerable items)
+ {
+ // Start at
+ if (request.StartIndex.HasValue)
+ {
+ items = items.Skip(request.StartIndex.Value);
+ }
+
+ // Return limit
+ if (request.Limit.HasValue)
+ {
+ items = items.Take(request.Limit.Value);
+ }
+
+ return items;
+ }
+
+ /// Gets query result.
+ /// The request.
+ /// The query result.
+ private async Task> GetQueryResult(BaseReportRequest request)
+ {
+ // Placeholder in case needed later
+ request.Recursive = true;
+ var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+ request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts";
+
+ var parentItem = string.IsNullOrEmpty(request.ParentId) ?
+ (user == null ? _libraryManager.RootFolder : user.RootFolder) :
+ _libraryManager.GetItemById(request.ParentId);
+
+ var item = string.IsNullOrEmpty(request.ParentId) ?
+ user == null ? _libraryManager.RootFolder : user.RootFolder :
+ parentItem;
+
+ IEnumerable items;
+
+ if (request.Recursive)
+ {
+ var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+ return result;
+ }
+ else
+ {
+ if (user == null)
+ {
+ var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
+ return result;
+ }
+
+ var userRoot = item as UserRootFolder;
+
+ if (userRoot == null)
+ {
+ var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+
+ return result;
+ }
+
+ items = ((Folder)item).GetChildren(user, true);
+ }
+
+ return new QueryResult { Items = items.ToArray() };
+
+ }
+
+ /// Gets report activities.
+ /// The request.
+ /// The report activities.
+ private Task GetReportActivities(IReportsDownload request)
+ {
+ return Task.Run(() =>
+ {
+ DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ?
+ (DateTime?)null :
+ DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
+
+ QueryResult queryResult;
+ if (request.HasQueryLimit)
+ queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, request.Limit);
+ else
+ queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, null);
+ //var queryResult = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit);
+
+ ReportActivitiesBuilder builder = new ReportActivitiesBuilder(_libraryManager, _userManager);
+ var result = builder.GetResult(queryResult, request);
+ result.TotalRecordCount = queryResult.TotalRecordCount;
+ return result;
+
+ });
+
+ }
+
+ /// Gets report result.
+ /// The request.
+ /// The report result.
+ private async Task GetReportResult(GetItemReport request)
+ {
+ ReportBuilder reportBuilder = new ReportBuilder(_libraryManager);
+ QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false);
+ ReportResult reportResult = reportBuilder.GetResult(queryResult.Items, request);
+ reportResult.TotalRecordCount = queryResult.TotalRecordCount;
+
+ return reportResult;
+ }
+
+ /// Gets report statistic.
+ /// The request.
+ /// The report statistic.
+ private async Task GetReportStatistic(GetReportStatistics request)
+ {
+ ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
+ QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false);
+
+ ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager);
+ ReportStatResult reportResult = reportBuilder.GetResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5);
+ reportResult.TotalRecordCount = reportResult.Groups.Count();
+ return reportResult;
+ }
+
+ #endregion
+
+ }
}
diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
index 541bb92d9f..c9ee6337ff 100644
--- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
+++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
@@ -9,206 +9,276 @@ using System.Threading.Tasks;
namespace MediaBrowser.Api.Reports
{
- /// A report stat builder.
- ///
- public class ReportStatBuilder : ReportBuilderBase
- {
- ///
- /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class.
- /// Manager for library.
- public ReportStatBuilder(ILibraryManager libraryManager)
- : base(libraryManager)
- {
- }
-
- /// Gets report stat result.
- /// The items.
- /// Type of the report row.
- /// The top item.
- /// The report stat result.
- public ReportStatResult GetReportStatResult(BaseItem[] items, ReportViewType reportRowType, int topItem = 5)
- {
- ReportStatResult result = new ReportStatResult();
- result = this.GetResultGenres(result, items, topItem);
- result = this.GetResultStudios(result, items, topItem);
- result = this.GetResultPersons(result, items, topItem);
- result = this.GetResultProductionYears(result, items, topItem);
- result = this.GetResulProductionLocations(result, items, topItem);
- result = this.GetResultCommunityRatings(result, items, topItem);
- result = this.GetResultParentalRatings(result, items, topItem);
-
- switch (reportRowType)
- {
- case ReportViewType.Season:
- case ReportViewType.Series:
- case ReportViewType.MusicAlbum:
- case ReportViewType.MusicArtist:
- case ReportViewType.Game:
- break;
- case ReportViewType.Movie:
- case ReportViewType.BoxSet:
-
- break;
- case ReportViewType.Book:
- case ReportViewType.Episode:
- case ReportViewType.Video:
- case ReportViewType.MusicVideo:
- case ReportViewType.Trailer:
- case ReportViewType.Audio:
- case ReportViewType.BaseItem:
- default:
- break;
- }
-
- result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList();
-
- return result;
- }
-
- private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderGenres"), topItem,
- items.SelectMany(x => x.Genres)
- .GroupBy(x => x)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key,
- Value = x.Count().ToString(),
- Id = GetGenreID(x.Key)
- }));
- return result;
-
- }
-
- private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderStudios"), topItem,
- items.SelectMany(x => x.Studios)
- .GroupBy(x => x)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key,
- Value = x.Count().ToString(),
- Id = GetStudioID(x.Key)
- })
- );
-
- return result;
-
- }
-
- private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- List t = new List { PersonType.Actor, PersonType.Composer, PersonType.Director, PersonType.GuestStar, PersonType.Producer, PersonType.Writer, "Artist", "AlbumArtist" };
- foreach (var item in t)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem,
- items.SelectMany(x => _libraryManager.GetPeople(x))
- .Where(n => n.Type == item)
- .GroupBy(x => x.Name)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key,
- Value = x.Count().ToString(),
- Id = GetPersonID(x.Key)
- })
- );
- }
-
- return result;
- }
-
- private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("LabelCommunityRating"), topItem,
- items.Where(x => x.CommunityRating != null && x.CommunityRating > 0)
- .GroupBy(x => x.CommunityRating)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key.ToString(),
- Value = x.Count().ToString()
- })
- );
-
- return result;
- }
-
- private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderParentalRatings"), topItem,
- items.Where(x => x.OfficialRating != null)
- .GroupBy(x => x.OfficialRating)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key.ToString(),
- Value = x.Count().ToString()
- })
- );
-
- return result;
- }
-
-
- private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderYears"), topItem,
- items.Where(x => x.ProductionYear != null && x.ProductionYear > 0)
- .GroupBy(x => x.ProductionYear)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key.ToString(),
- Value = x.Count().ToString()
- })
- );
-
- return result;
- }
-
- private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5)
- {
- this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderCountries"), topItem,
- items.OfType()
- .Where(x => x.ProductionLocations != null)
- .SelectMany(x => x.ProductionLocations)
- .GroupBy(x => x)
- .OrderByDescending(x => x.Count())
- .Take(topItem)
- .Select(x => new ReportStatItem
- {
- Name = x.Key.ToString(),
- Value = x.Count().ToString()
- })
- );
-
- return result;
- }
-
-
- /// Gets the groups.
- /// The result.
- /// The header.
- /// The top item.
- /// The top.
- private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable top)
- {
- if (top.Count() > 0)
- {
- var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) };
- group.Items.AddRange(top);
- result.Groups.Add(group);
- }
- }
- }
+ /// A report stat builder.
+ ///
+ public class ReportStatBuilder : ReportBuilderBase
+ {
+ #region [Constructors]
+
+ ///
+ /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class.
+ /// Manager for library.
+ public ReportStatBuilder(ILibraryManager libraryManager)
+ : base(libraryManager)
+ {
+ }
+
+ #endregion
+
+ #region [Public Methods]
+
+ /// Gets report stat result.
+ /// The items.
+ /// List of types of the report include items.
+ /// The top item.
+ /// The report stat result.
+ public ReportStatResult GetResult(BaseItem[] items, ReportIncludeItemTypes reportIncludeItemTypes, int topItem = 5)
+ {
+ ReportStatResult result = new ReportStatResult();
+ result = this.GetResultGenres(result, items, topItem);
+ result = this.GetResultStudios(result, items, topItem);
+ result = this.GetResultPersons(result, items, topItem);
+ result = this.GetResultProductionYears(result, items, topItem);
+ result = this.GetResulProductionLocations(result, items, topItem);
+ result = this.GetResultCommunityRatings(result, items, topItem);
+ result = this.GetResultParentalRatings(result, items, topItem);
+
+ switch (reportIncludeItemTypes)
+ {
+ case ReportIncludeItemTypes.Season:
+ case ReportIncludeItemTypes.Series:
+ case ReportIncludeItemTypes.MusicAlbum:
+ case ReportIncludeItemTypes.MusicArtist:
+ case ReportIncludeItemTypes.Game:
+ break;
+ case ReportIncludeItemTypes.Movie:
+ case ReportIncludeItemTypes.BoxSet:
+
+ break;
+ case ReportIncludeItemTypes.Book:
+ case ReportIncludeItemTypes.Episode:
+ case ReportIncludeItemTypes.Video:
+ case ReportIncludeItemTypes.MusicVideo:
+ case ReportIncludeItemTypes.Trailer:
+ case ReportIncludeItemTypes.Audio:
+ case ReportIncludeItemTypes.BaseItem:
+ default:
+ break;
+ }
+
+ result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList();
+
+ return result;
+ }
+
+ #endregion
+
+ #region [Protected Internal Methods]
+ /// Gets the headers.
+ /// Type of the header.
+ /// The request.
+ /// The headers.
+ ///
+ protected internal override List GetHeaders(H request)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region [Private Methods]
+
+ /// Gets the groups.
+ /// The result.
+ /// The header.
+ /// The top item.
+ /// The top.
+ private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable top)
+ {
+ if (top != null && top.Count() > 0)
+ {
+ var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) };
+ group.Items.AddRange(top);
+ result.Groups.Add(group);
+ }
+ }
+
+ /// Gets resul production locations.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The resul production locations.
+ private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Countries), topItem,
+ items.OfType()
+ .Where(x => x.ProductionLocations != null)
+ .SelectMany(x => x.ProductionLocations)
+ .GroupBy(x => x)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem)
+ .Select(x => new ReportStatItem
+ {
+ Name = x.Key.ToString(),
+ Value = x.Count().ToString()
+ })
+ );
+
+ return result;
+ }
+
+ /// Gets result community ratings.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The result community ratings.
+ private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.CommunityRating), topItem,
+ items.Where(x => x.CommunityRating != null && x.CommunityRating > 0)
+ .GroupBy(x => x.CommunityRating)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem)
+ .Select(x => new ReportStatItem
+ {
+ Name = x.Key.ToString(),
+ Value = x.Count().ToString()
+ })
+ );
+
+ return result;
+ }
+
+ /// Gets result genres.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The result genres.
+ private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Genres), topItem,
+ items.SelectMany(x => x.Genres)
+ .GroupBy(x => x)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem)
+ .Select(x => new ReportStatItem
+ {
+ Name = x.Key,
+ Value = x.Count().ToString(),
+ Id = GetGenreID(x.Key)
+ }));
+ return result;
+
+ }
+
+ /// Gets result parental ratings.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The result parental ratings.
+ private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.ParentalRatings), topItem,
+ items.Where(x => x.OfficialRating != null)
+ .GroupBy(x => x.OfficialRating)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem)
+ .Select(x => new ReportStatItem
+ {
+ Name = x.Key.ToString(),
+ Value = x.Count().ToString()
+ })
+ );
+
+ return result;
+ }
+
+ /// Gets result persons.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The result persons.
+ private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ List t = new List
+ {
+ HeaderMetadata.Actor,
+ HeaderMetadata.Composer,
+ HeaderMetadata.Director,
+ HeaderMetadata.GuestStar,
+ HeaderMetadata.Producer,
+ HeaderMetadata.Writer,
+ HeaderMetadata.Artist,
+ HeaderMetadata.AlbumArtist
+ };
+ foreach (var item in t)
+ {
+ var ps = items.Where(x => x.People != null && x.SupportsPeople).SelectMany(x => x.People)
+ .Where(n => n.Type == item.ToString())
+ .GroupBy(x => x.Name)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem);
+ if (ps != null && ps.Count() > 0)
+ this.GetGroups(result, GetLocalizedHeader(item), topItem,
+ ps.Select(x => new ReportStatItem
+ {
+ Name = x.Key,
+ Value = x.Count().ToString(),
+ Id = GetPersonID(x.Key)
+ })
+ );
+ }
+
+ return result;
+ }
+
+ /// Gets result production years.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The result production years.
+ private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Year), topItem,
+ items.Where(x => x.ProductionYear != null && x.ProductionYear > 0)
+ .GroupBy(x => x.ProductionYear)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem)
+ .Select(x => new ReportStatItem
+ {
+ Name = x.Key.ToString(),
+ Value = x.Count().ToString()
+ })
+ );
+
+ return result;
+ }
+
+ /// Gets result studios.
+ /// The result.
+ /// The items.
+ /// The top item.
+ /// The result studios.
+ private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5)
+ {
+ this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Studios), topItem,
+ items.SelectMany(x => x.Studios)
+ .GroupBy(x => x)
+ .OrderByDescending(x => x.Count())
+ .Take(topItem)
+ .Select(x => new ReportStatItem
+ {
+ Name = x.Key,
+ Value = x.Count().ToString(),
+ Id = GetStudioID(x.Key)
+ })
+ );
+
+ return result;
+
+ }
+
+ #endregion
+
+ }
}
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index 2cca72593b..302b8d834c 100644
--- a/MediaBrowser.Api/SearchService.cs
+++ b/MediaBrowser.Api/SearchService.cs
@@ -4,6 +4,7 @@ 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;
@@ -171,6 +172,8 @@ namespace MediaBrowser.Api
ProductionYear = item.ProductionYear
};
+ result.ChannelId = item.ChannelId;
+
var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
if (primaryImageTag != null)
@@ -181,24 +184,19 @@ namespace MediaBrowser.Api
SetThumbImageInfo(result, item);
SetBackdropImageInfo(result, item);
- var episode = item as Episode;
-
- if (episode != null)
+ var hasSeries = item as IHasSeries;
+ if (hasSeries != null)
{
- result.Series = episode.Series.Name;
+ result.Series = hasSeries.SeriesName;
}
var season = item as Season;
-
if (season != null)
{
- result.Series = season.Series.Name;
-
result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count;
}
var series = item as Series;
-
if (series != null)
{
result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count;
@@ -223,6 +221,12 @@ namespace MediaBrowser.Api
result.Artists = song.Artists.ToArray();
}
+ if (!string.IsNullOrWhiteSpace(item.ChannelId))
+ {
+ var channel = _libraryManager.GetItemById(item.ChannelId);
+ result.ChannelName = channel == null ? null : channel.Name;
+ }
+
return result;
}
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index 6ee8d36035..e9ac45fa25 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -3,8 +3,10 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.LiveTv;
using ServiceStack;
using System;
using System.Linq;
@@ -49,13 +51,15 @@ namespace MediaBrowser.Api
private readonly IServerApplicationHost _appHost;
private readonly IUserManager _userManager;
private readonly IConnectManager _connectManager;
+ private readonly ILiveTvManager _liveTvManager;
- public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager)
+ public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, ILiveTvManager liveTvManager)
{
_config = config;
_appHost = appHost;
_userManager = userManager;
_connectManager = connectManager;
+ _liveTvManager = liveTvManager;
}
public void Post(ReportStartupWizardComplete request)
@@ -67,6 +71,8 @@ namespace MediaBrowser.Api
_config.Configuration.EnableLibraryMetadataSubFolder = true;
_config.Configuration.EnableUserSpecificUserViews = true;
_config.Configuration.EnableCustomPathSubFolders = true;
+ _config.Configuration.DisableXmlSavers = true;
+ _config.Configuration.DisableStartupScan = true;
_config.SaveConfiguration();
}
@@ -82,7 +88,7 @@ namespace MediaBrowser.Api
public object Get(GetStartupConfiguration request)
{
- return new StartupConfiguration
+ var result = new StartupConfiguration
{
UICulture = _config.Configuration.UICulture,
EnableInternetProviders = _config.Configuration.EnableInternetProviders,
@@ -90,6 +96,22 @@ namespace MediaBrowser.Api
MetadataCountryCode = _config.Configuration.MetadataCountryCode,
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
};
+
+ var tvConfig = GetLiveTVConfiguration();
+
+ if (tvConfig.TunerHosts.Count > 0)
+ {
+ result.LiveTvTunerPath = tvConfig.TunerHosts[0].Url;
+ result.LiveTvTunerType = tvConfig.TunerHosts[0].Type;
+ }
+
+ if (tvConfig.ListingProviders.Count > 0)
+ {
+ result.LiveTvGuideProviderId = tvConfig.ListingProviders[0].Id;
+ result.LiveTvGuideProviderType = tvConfig.ListingProviders[0].Type;
+ }
+
+ return result;
}
public void Post(UpdateStartupConfiguration request)
@@ -100,6 +122,9 @@ namespace MediaBrowser.Api
_config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
_config.SaveConfiguration();
+
+ var task = UpdateTuners(request);
+ Task.WaitAll(task);
}
public object Get(GetStartupUser request)
@@ -140,6 +165,51 @@ namespace MediaBrowser.Api
return result;
}
+
+ private async Task UpdateTuners(UpdateStartupConfiguration request)
+ {
+ var config = GetLiveTVConfiguration();
+ var save = false;
+
+ if (string.IsNullOrWhiteSpace(request.LiveTvTunerPath) ||
+ string.IsNullOrWhiteSpace(request.LiveTvTunerType))
+ {
+ if (config.TunerHosts.Count > 0)
+ {
+ config.TunerHosts.Clear();
+ save = true;
+ }
+ }
+ else
+ {
+ if (!config.TunerHosts.Any(i => string.Equals(i.Type, request.LiveTvTunerType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.Url, request.LiveTvTunerPath, StringComparison.OrdinalIgnoreCase)))
+ {
+ // Add tuner
+ await _liveTvManager.SaveTunerHost(new TunerHostInfo
+ {
+ IsEnabled = true,
+ Type = request.LiveTvTunerType,
+ Url = request.LiveTvTunerPath
+
+ }).ConfigureAwait(false);
+ }
+ }
+
+ if (save)
+ {
+ SaveLiveTVConfiguration(config);
+ }
+ }
+
+ private void SaveLiveTVConfiguration(LiveTvOptions config)
+ {
+ _config.SaveConfiguration("livetv", config);
+ }
+
+ private LiveTvOptions GetLiveTVConfiguration()
+ {
+ return _config.GetConfiguration("livetv");
+ }
}
public class StartupConfiguration
@@ -149,6 +219,10 @@ namespace MediaBrowser.Api
public bool SaveLocalMeta { get; set; }
public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
+ public string LiveTvTunerType { get; set; }
+ public string LiveTvTunerPath { get; set; }
+ public string LiveTvGuideProviderId { get; set; }
+ public string LiveTvGuideProviderType { get; set; }
}
public class StartupInfo
diff --git a/MediaBrowser.Api/Sync/SyncHelper.cs b/MediaBrowser.Api/Sync/SyncHelper.cs
index 6a5b6a927a..0d3e8707db 100644
--- a/MediaBrowser.Api/Sync/SyncHelper.cs
+++ b/MediaBrowser.Api/Sync/SyncHelper.cs
@@ -10,11 +10,6 @@ namespace MediaBrowser.Api.Sync
{
List options = new List();
- if (items.Count > 1)
- {
- options.Add(SyncJobOption.Name);
- }
-
foreach (BaseItemDto item in items)
{
if (item.SupportsSync ?? false)
@@ -65,7 +60,6 @@ namespace MediaBrowser.Api.Sync
{
List options = new List();
- options.Add(SyncJobOption.Name);
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.Profile);
options.Add(SyncJobOption.UnwatchedOnly);
diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
index 2393d05335..cde5eade5b 100644
--- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using ServiceStack;
-using System;
using System.Collections.Generic;
using System.Linq;
@@ -124,48 +123,18 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
if (request is GetAlbumArtists)
{
- return items
- .Where(i => !i.IsFolder)
- .OfType()
- .SelectMany(i => i.AlbumArtists)
- .DistinctNames()
- .Select(name =>
- {
- try
- {
- return LibraryManager.GetArtist(name);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting artist {0}", ex, name);
- return null;
- }
-
- }).Where(i => i != null);
+ return LibraryManager.GetAlbumArtists(items
+ .Where(i => !i.IsFolder)
+ .OfType());
}
- return items
+ return LibraryManager.GetArtists(items
.Where(i => !i.IsFolder)
- .OfType()
- .SelectMany(i => i.AllArtists)
- .DistinctNames()
- .Select(name =>
- {
- try
- {
- return LibraryManager.GetArtist(name);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting artist {0}", ex, name);
- return null;
- }
-
- }).Where(i => i != null);
+ .OfType());
}
}
}
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 8084fd083b..a282029433 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -48,6 +48,42 @@ namespace MediaBrowser.Api.UserLibrary
DtoService = dtoService;
}
+ protected BaseItem GetParentItem(GetItemsByName request)
+ {
+ BaseItem parentItem;
+
+ if (!string.IsNullOrWhiteSpace(request.UserId))
+ {
+ var user = UserManager.GetUserById(request.UserId);
+ parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
+ }
+ else
+ {
+ parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
+ }
+
+ return parentItem;
+ }
+
+ protected string GetParentItemViewType(GetItemsByName request)
+ {
+ var parent = GetParentItem(request);
+
+ var collectionFolder = parent as ICollectionFolder;
+ if (collectionFolder != null)
+ {
+ return collectionFolder.CollectionType;
+ }
+
+ var view = parent as UserView;
+ if (view != null)
+ {
+ return view.ViewType;
+ }
+
+ return null;
+ }
+
///
/// Gets the specified request.
///
@@ -114,13 +150,13 @@ namespace MediaBrowser.Api.UserLibrary
var filteredItems = FilterItems(request, extractedItems, user);
- filteredItems = FilterByLibraryItems(request, filteredItems, user, libraryItems);
+ filteredItems = FilterByLibraryItems(request, filteredItems.Cast(), user, libraryItems).Cast();
- filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending).Cast();
+ filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending);
var ibnItemsArray = filteredItems.ToList();
- IEnumerable ibnItems = ibnItemsArray;
+ IEnumerable ibnItems = ibnItemsArray;
var result = new ItemsResult
{
@@ -141,14 +177,14 @@ namespace MediaBrowser.Api.UserLibrary
}
- IEnumerable>> tuples;
+ IEnumerable>> tuples;
if (dtoOptions.Fields.Contains(ItemFields.ItemCounts))
{
- tuples = ibnItems.Select(i => new Tuple>(i, i.GetTaggedItems(libraryItems).ToList()));
+ tuples = ibnItems.Select(i => new Tuple>(i, ((IItemByName)i).GetTaggedItems(libraryItems).ToList()));
}
else
{
- tuples = ibnItems.Select(i => new Tuple>(i, new List()));
+ tuples = ibnItems.Select(i => new Tuple>(i, new List()));
}
var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user));
@@ -180,7 +216,7 @@ namespace MediaBrowser.Api.UserLibrary
return options.Fields.Contains(ItemFields.ItemCounts);
}
- private IEnumerable FilterByLibraryItems(GetItemsByName request, IEnumerable items, User user, IEnumerable libraryItems)
+ private IEnumerable FilterByLibraryItems(GetItemsByName request, IEnumerable items, User user, IEnumerable libraryItems)
{
var filters = request.GetFilters().ToList();
@@ -211,7 +247,7 @@ namespace MediaBrowser.Api.UserLibrary
/// The items.
/// The user.
/// IEnumerable{`0}.
- private IEnumerable FilterItems(GetItemsByName request, IEnumerable items, User user)
+ private IEnumerable FilterItems(GetItemsByName request, IEnumerable items, User user)
{
if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater))
{
@@ -375,7 +411,7 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Task{`0}}.
- protected abstract IEnumerable GetAllItems(GetItemsByName request, IEnumerable items);
+ protected abstract IEnumerable GetAllItems(GetItemsByName request, IEnumerable items);
}
///
@@ -383,22 +419,6 @@ namespace MediaBrowser.Api.UserLibrary
///
public class GetItemsByName : BaseItemsRequest, IReturn
{
- ///
- /// Gets or sets the user id.
- ///
- /// The user id.
- [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UserId { get; set; }
-
- [ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string NameStartsWithOrGreater { get; set; }
-
- [ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string NameStartsWith { get; set; }
-
- [ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is sorted less than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string NameLessThan { get; set; }
-
public GetItemsByName()
{
Recursive = true;
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index fffc11d68f..7db8e4dcad 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -14,6 +14,97 @@ namespace MediaBrowser.Api.UserLibrary
EnableImages = true;
}
+ ///
+ /// Gets or sets the max offical rating.
+ ///
+ /// The max offical rating.
+ [ApiMember(Name = "MaxOfficialRating", Description = "Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string MaxOfficialRating { get; set; }
+
+ [ApiMember(Name = "HasThemeSong", Description = "Optional filter by items with theme songs.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? HasThemeSong { get; set; }
+
+ [ApiMember(Name = "HasThemeVideo", Description = "Optional filter by items with theme videos.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? HasThemeVideo { get; set; }
+
+ [ApiMember(Name = "HasSubtitles", Description = "Optional filter by items with subtitles.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? HasSubtitles { get; set; }
+
+ [ApiMember(Name = "HasSpecialFeature", Description = "Optional filter by items with special features.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? HasSpecialFeature { get; set; }
+
+ [ApiMember(Name = "HasTrailer", Description = "Optional filter by items with trailers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? HasTrailer { get; set; }
+
+ [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string AdjacentTo { get; set; }
+
+ [ApiMember(Name = "MinIndexNumber", Description = "Optional filter by minimum index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? MinIndexNumber { get; set; }
+
+ [ApiMember(Name = "MinPlayers", Description = "Optional filter by minimum number of game players.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? MinPlayers { get; set; }
+
+ [ApiMember(Name = "MaxPlayers", Description = "Optional filter by maximum number of game players.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? MaxPlayers { get; set; }
+
+ [ApiMember(Name = "ParentIndexNumber", Description = "Optional filter by parent index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ParentIndexNumber { get; set; }
+
+ [ApiMember(Name = "HasParentalRating", Description = "Optional filter by items that have or do not have a parental rating", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? HasParentalRating { get; set; }
+
+ [ApiMember(Name = "IsHD", Description = "Optional filter by items that are HD or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsHD { get; set; }
+
+ [ApiMember(Name = "LocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string LocationTypes { get; set; }
+
+ [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string ExcludeLocationTypes { get; set; }
+
+ [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsMissing { get; set; }
+
+ [ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsUnaired { get; set; }
+
+ [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsVirtualUnaired { get; set; }
+
+ [ApiMember(Name = "MinCommunityRating", Description = "Optional filter by minimum community rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public double? MinCommunityRating { get; set; }
+
+ [ApiMember(Name = "MinCriticRating", Description = "Optional filter by minimum critic rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public double? MinCriticRating { get; set; }
+
+ [ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? AiredDuringSeason { get; set; }
+
+ [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string MinPremiereDate { get; set; }
+
+ [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string MaxPremiereDate { get; set; }
+
+ [ApiMember(Name = "HasOverview", Description = "Optional filter by items that have an overview or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? HasOverview { get; set; }
+
+ [ApiMember(Name = "HasImdbId", Description = "Optional filter by items that have an imdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? HasImdbId { get; set; }
+
+ [ApiMember(Name = "HasTmdbId", Description = "Optional filter by items that have a tmdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? HasTmdbId { get; set; }
+
+ [ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? HasTvdbId { get; set; }
+
+ [ApiMember(Name = "IsYearMismatched", Description = "Optional filter by items that are potentially misidentified.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsYearMismatched { get; set; }
+
+ [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsInBoxSet { get; set; }
+
///
/// Skips over a given number of items within the results. Use for paging.
///
@@ -130,6 +221,121 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+ ///
+ /// Limit results to items containing a specific person
+ ///
+ /// The person.
+ [ApiMember(Name = "Person", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Person { get; set; }
+
+ [ApiMember(Name = "PersonIds", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string PersonIds { get; set; }
+
+ ///
+ /// If the Person filter is used, this can also be used to restrict to a specific person type
+ ///
+ /// The type of the person.
+ [ApiMember(Name = "PersonTypes", Description = "Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string PersonTypes { get; set; }
+
+ ///
+ /// Limit results to items containing specific studios
+ ///
+ /// The studios.
+ [ApiMember(Name = "Studios", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Studios { get; set; }
+
+ [ApiMember(Name = "StudioIds", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string StudioIds { get; set; }
+
+ ///
+ /// Gets or sets the studios.
+ ///
+ /// The studios.
+ [ApiMember(Name = "Artists", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Artists { get; set; }
+
+ [ApiMember(Name = "ArtistIds", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string ArtistIds { get; set; }
+
+ [ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Albums { get; set; }
+
+ ///
+ /// Gets or sets the item ids.
+ ///
+ /// The item ids.
+ [ApiMember(Name = "Ids", Description = "Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string Ids { get; set; }
+
+ ///
+ /// Gets or sets the video types.
+ ///
+ /// The video types.
+ [ApiMember(Name = "VideoTypes", Description = "Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string VideoTypes { get; set; }
+
+ ///
+ /// Gets or sets the air days.
+ ///
+ /// The air days.
+ [ApiMember(Name = "AirDays", Description = "Optional filter by Series Air Days. Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string AirDays { get; set; }
+
+ ///
+ /// Gets or sets the user id.
+ ///
+ /// The user id.
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string UserId { get; set; }
+
+ ///
+ /// Gets or sets the min offical rating.
+ ///
+ /// The min offical rating.
+ [ApiMember(Name = "MinOfficialRating", Description = "Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string MinOfficialRating { get; set; }
+
+ [ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? IsLocked { get; set; }
+
+ [ApiMember(Name = "IsUnidentified", Description = "Optional filter by items that are unidentified by internet metadata providers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? IsUnidentified { get; set; }
+
+ [ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? IsPlaceHolder { get; set; }
+
+ [ApiMember(Name = "HasOfficialRating", Description = "Optional filter by items that have official ratings", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public bool? HasOfficialRating { get; set; }
+
+ [ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? CollapseBoxSetItems { get; set; }
+ ///
+ /// Gets or sets the video formats.
+ ///
+ /// The video formats.
+ [ApiMember(Name = "Is3D", Description = "Optional filter by items that are 3D, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? Is3D { get; set; }
+
+ ///
+ /// Gets or sets the series status.
+ ///
+ /// The series status.
+ [ApiMember(Name = "SeriesStatus", Description = "Optional filter by Series Status. Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+ public string SeriesStatus { get; set; }
+
+ [ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string NameStartsWithOrGreater { get; set; }
+
+ [ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string NameStartsWith { get; set; }
+
+ [ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is equally or lesser than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string NameLessThan { get; set; }
+
+ [ApiMember(Name = "AlbumArtistStartsWithOrGreater", Description = "Optional filter by items whose album artist is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string AlbumArtistStartsWithOrGreater { get; set; }
+
public string[] GetGenres()
{
return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
@@ -164,6 +370,43 @@ namespace MediaBrowser.Api.UserLibrary
{
return (Years ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
}
+
+ public string[] GetStudios()
+ {
+ return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ public string[] GetStudioIds()
+ {
+ return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ public string[] GetPersonTypes()
+ {
+ return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ public string[] GetPersonIds()
+ {
+ return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ public string[] GetItemIds()
+ {
+ return (Ids ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ public VideoType[] GetVideoTypes()
+ {
+ var val = VideoTypes;
+
+ if (string.IsNullOrEmpty(val))
+ {
+ return new VideoType[] { };
+ }
+
+ return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray();
+ }
///
/// Gets the filters.
diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
index f9d0f0d0fa..a1ad14a4de 100644
--- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
@@ -99,14 +99,24 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
- var itemsList = items.Where(i => i.Genres != null).ToList();
-
- return itemsList
+ return items
.SelectMany(i => i.Genres)
.DistinctNames()
- .Select(name => LibraryManager.GetGameGenre(name));
+ .Select(name =>
+ {
+ try
+ {
+ return LibraryManager.GetGameGenre(name);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting genre {0}", ex, name);
+ return null;
+ }
+ })
+ .Where(i => i != null);
}
}
}
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index 0702468861..d383bd0adb 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -1,11 +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.Querying;
+using MediaBrowser.Model.Entities;
using ServiceStack;
using System;
using System.Collections.Generic;
@@ -104,8 +103,38 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
+ var viewType = GetParentItemViewType(request);
+
+ if (string.Equals(viewType, CollectionType.Music) || string.Equals(viewType, CollectionType.MusicVideos))
+ {
+ return items
+ .SelectMany(i => i.Genres)
+ .DistinctNames()
+ .Select(name => LibraryManager.GetMusicGenre(name));
+ }
+
+ if (string.Equals(viewType, CollectionType.Games))
+ {
+ return items
+ .SelectMany(i => i.Genres)
+ .DistinctNames()
+ .Select(name =>
+ {
+ try
+ {
+ return LibraryManager.GetGameGenre(name);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting genre {0}", ex, name);
+ return null;
+ }
+ })
+ .Where(i => i != null);
+ }
+
return items
.SelectMany(i => i.Genres)
.DistinctNames()
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index fda933b593..7d3290c2f9 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -2,7 +2,6 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
@@ -25,249 +24,6 @@ namespace MediaBrowser.Api.UserLibrary
[Route("/Users/{UserId}/Items", "GET", Summary = "Gets items based on a query.")]
public class GetItems : BaseItemsRequest, IReturn
{
- ///
- /// Gets or sets the user id.
- ///
- /// The user id.
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UserId { get; set; }
-
- ///
- /// Limit results to items containing a specific person
- ///
- /// The person.
- [ApiMember(Name = "Person", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string Person { get; set; }
-
- [ApiMember(Name = "PersonIds", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string PersonIds { get; set; }
-
- ///
- /// If the Person filter is used, this can also be used to restrict to a specific person type
- ///
- /// The type of the person.
- [ApiMember(Name = "PersonTypes", Description = "Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string PersonTypes { get; set; }
-
- ///
- /// Limit results to items containing specific studios
- ///
- /// The studios.
- [ApiMember(Name = "Studios", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string Studios { get; set; }
-
- [ApiMember(Name = "StudioIds", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string StudioIds { get; set; }
-
- ///
- /// Gets or sets the studios.
- ///
- /// The studios.
- [ApiMember(Name = "Artists", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string Artists { get; set; }
-
- [ApiMember(Name = "ArtistIds", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string ArtistIds { get; set; }
-
- [ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string Albums { get; set; }
-
- ///
- /// Gets or sets the item ids.
- ///
- /// The item ids.
- [ApiMember(Name = "Ids", Description = "Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string Ids { get; set; }
-
- ///
- /// Gets or sets the video types.
- ///
- /// The video types.
- [ApiMember(Name = "VideoTypes", Description = "Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string VideoTypes { get; set; }
-
- ///
- /// Gets or sets the video formats.
- ///
- /// The video formats.
- [ApiMember(Name = "Is3D", Description = "Optional filter by items that are 3D, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? Is3D { get; set; }
-
- ///
- /// Gets or sets the series status.
- ///
- /// The series status.
- [ApiMember(Name = "SeriesStatus", Description = "Optional filter by Series Status. Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string SeriesStatus { get; set; }
-
- [ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string NameStartsWithOrGreater { get; set; }
-
- [ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string NameStartsWith { get; set; }
-
- [ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is equally or lesser than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string NameLessThan { get; set; }
-
- [ApiMember(Name = "AlbumArtistStartsWithOrGreater", Description = "Optional filter by items whose album artist is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string AlbumArtistStartsWithOrGreater { get; set; }
-
- ///
- /// Gets or sets the air days.
- ///
- /// The air days.
- [ApiMember(Name = "AirDays", Description = "Optional filter by Series Air Days. Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string AirDays { get; set; }
-
- ///
- /// Gets or sets the min offical rating.
- ///
- /// The min offical rating.
- [ApiMember(Name = "MinOfficialRating", Description = "Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string MinOfficialRating { get; set; }
-
- ///
- /// Gets or sets the max offical rating.
- ///
- /// The max offical rating.
- [ApiMember(Name = "MaxOfficialRating", Description = "Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string MaxOfficialRating { get; set; }
-
- [ApiMember(Name = "HasThemeSong", Description = "Optional filter by items with theme songs.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasThemeSong { get; set; }
-
- [ApiMember(Name = "HasThemeVideo", Description = "Optional filter by items with theme videos.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasThemeVideo { get; set; }
-
- [ApiMember(Name = "HasSubtitles", Description = "Optional filter by items with subtitles.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasSubtitles { get; set; }
-
- [ApiMember(Name = "HasSpecialFeature", Description = "Optional filter by items with special features.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasSpecialFeature { get; set; }
-
- [ApiMember(Name = "HasTrailer", Description = "Optional filter by items with trailers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasTrailer { get; set; }
-
- [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string AdjacentTo { get; set; }
-
- [ApiMember(Name = "MinIndexNumber", Description = "Optional filter by minimum index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MinIndexNumber { get; set; }
-
- [ApiMember(Name = "MinPlayers", Description = "Optional filter by minimum number of game players.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MinPlayers { get; set; }
-
- [ApiMember(Name = "MaxPlayers", Description = "Optional filter by maximum number of game players.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? MaxPlayers { get; set; }
-
- [ApiMember(Name = "ParentIndexNumber", Description = "Optional filter by parent index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? ParentIndexNumber { get; set; }
-
- [ApiMember(Name = "HasParentalRating", Description = "Optional filter by items that have or do not have a parental rating", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasParentalRating { get; set; }
-
- [ApiMember(Name = "IsHD", Description = "Optional filter by items that are HD or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsHD { get; set; }
-
- [ApiMember(Name = "LocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string LocationTypes { get; set; }
-
- [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string ExcludeLocationTypes { get; set; }
-
- [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsMissing { get; set; }
-
- [ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsUnaired { get; set; }
-
- [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsVirtualUnaired { get; set; }
-
- [ApiMember(Name = "MinCommunityRating", Description = "Optional filter by minimum community rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public double? MinCommunityRating { get; set; }
-
- [ApiMember(Name = "MinCriticRating", Description = "Optional filter by minimum critic rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public double? MinCriticRating { get; set; }
-
- [ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
- public int? AiredDuringSeason { get; set; }
-
- [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string MinPremiereDate { get; set; }
-
- [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string MaxPremiereDate { get; set; }
-
- [ApiMember(Name = "HasOverview", Description = "Optional filter by items that have an overview or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasOverview { get; set; }
-
- [ApiMember(Name = "HasImdbId", Description = "Optional filter by items that have an imdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasImdbId { get; set; }
-
- [ApiMember(Name = "HasTmdbId", Description = "Optional filter by items that have a tmdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasTmdbId { get; set; }
-
- [ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? HasTvdbId { get; set; }
-
- [ApiMember(Name = "IsYearMismatched", Description = "Optional filter by items that are potentially misidentified.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsYearMismatched { get; set; }
-
- [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? IsInBoxSet { get; set; }
-
- [ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? IsLocked { get; set; }
-
- [ApiMember(Name = "IsUnidentified", Description = "Optional filter by items that are unidentified by internet metadata providers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? IsUnidentified { get; set; }
-
- [ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? IsPlaceHolder { get; set; }
-
- [ApiMember(Name = "HasOfficialRating", Description = "Optional filter by items that have official ratings", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public bool? HasOfficialRating { get; set; }
-
- [ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
- public bool? CollapseBoxSetItems { get; set; }
-
- public string[] GetStudios()
- {
- return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetStudioIds()
- {
- return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetPersonTypes()
- {
- return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetPersonIds()
- {
- return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public string[] GetItemIds()
- {
- return (Ids ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public VideoType[] GetVideoTypes()
- {
- var val = VideoTypes;
-
- if (string.IsNullOrEmpty(val))
- {
- return new VideoType[] { };
- }
-
- return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray();
- }
}
///
@@ -361,7 +117,16 @@ namespace MediaBrowser.Api.UserLibrary
if (!string.IsNullOrEmpty(request.Ids))
{
request.Recursive = true;
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+ var query = GetItemsQuery(request, user);
+ var result = await ((Folder)item).GetItems(query).ConfigureAwait(false);
+
+ if (string.IsNullOrWhiteSpace(request.SortBy))
+ {
+ var ids = query.ItemIds.ToList();
+
+ // Try to preserve order
+ result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
+ }
return new Tuple, bool>(result, true);
}
diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
index e63d6c0f47..1826915840 100644
--- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
@@ -99,11 +99,9 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
- var itemsList = items.ToList();
-
- return itemsList
+ return items
.SelectMany(i => i.Genres)
.DistinctNames()
.Select(name => LibraryManager.GetMusicGenre(name));
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index bd9898dcdf..e6a60fcc6d 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -16,12 +16,6 @@ namespace MediaBrowser.Api.UserLibrary
[Route("/Persons", "GET", Summary = "Gets all persons from a given item, folder, or the entire library")]
public class GetPersons : GetItemsByName
{
- ///
- /// Gets or sets the person types.
- ///
- /// The person types.
- [ApiMember(Name = "PersonTypes", Description = "Optional filter by person type. Accepts multiple, comma-delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
- public string PersonTypes { get; set; }
}
///
@@ -114,7 +108,7 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
var inputPersonTypes = ((GetPersons)request).PersonTypes;
var personTypes = string.IsNullOrEmpty(inputPersonTypes) ? new string[] { } : inputPersonTypes.Split(',');
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index 5f32725d8a..7002a37035 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -185,6 +185,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string PlaySessionId { get; set; }
+
+ [ApiMember(Name = "RepeatMode", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public RepeatMode RepeatMode { get; set; }
}
///
@@ -325,7 +328,8 @@ namespace MediaBrowser.Api.UserLibrary
VolumeLevel = request.VolumeLevel,
PlayMethod = request.PlayMethod,
PlaySessionId = request.PlaySessionId,
- LiveStreamId = request.LiveStreamId
+ LiveStreamId = request.LiveStreamId,
+ RepeatMode = request.RepeatMode
});
}
diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs
index d6a7db14fb..7cf8d752a4 100644
--- a/MediaBrowser.Api/UserLibrary/StudiosService.cs
+++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs
@@ -103,7 +103,7 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
var itemsList = items.Where(i => i.Studios != null).ToList();
diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
index 43d8dbc169..a49ab85562 100644
--- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
var views = user.RootFolder
.GetChildren(user, true)
.OfType()
- .Where(i => IsEligibleForSpecialView(i))
+ .Where(IsEligibleForSpecialView)
.ToList();
var list = views
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index 859b9f9592..4b3fe6c8e7 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -103,7 +103,7 @@ namespace MediaBrowser.Api.UserLibrary
/// The request.
/// The items.
/// IEnumerable{Tuple{System.StringFunc{System.Int32}}}.
- protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
+ protected override IEnumerable GetAllItems(GetItemsByName request, IEnumerable items)
{
var itemsList = items.Where(i => i.ProductionYear != null).ToList();
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
index ae2148f086..89f405e8a0 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -68,6 +68,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
+
+ // Trakt requests sometimes fail without this
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
}
///
@@ -124,7 +127,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private WebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
{
- var request = WebRequest.Create(options.Url);
+ var request = CreateWebRequest(options.Url);
var httpWebRequest = request as HttpWebRequest;
if (httpWebRequest != null)
@@ -432,7 +435,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
var httpResponse = (HttpWebResponse)response;
- EnsureSuccessStatusCode(httpResponse, options);
+ EnsureSuccessStatusCode(client, httpResponse, options);
options.CancellationToken.ThrowIfCancellationRequested();
@@ -443,7 +446,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
var httpResponse = (HttpWebResponse)response;
- EnsureSuccessStatusCode(httpResponse, options);
+ EnsureSuccessStatusCode(client, httpResponse, options);
options.CancellationToken.ThrowIfCancellationRequested();
@@ -629,7 +632,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
var httpResponse = (HttpWebResponse)response;
- EnsureSuccessStatusCode(httpResponse, options);
+ var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
+ EnsureSuccessStatusCode(client, httpResponse, options);
options.CancellationToken.ThrowIfCancellationRequested();
@@ -803,13 +807,20 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
return exception;
}
- private void EnsureSuccessStatusCode(HttpWebResponse response, HttpRequestOptions options)
+ private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options)
{
var statusCode = response.StatusCode;
+
var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299;
if (!isSuccessful)
{
+ if ((int) statusCode == 429)
+ {
+ client.LastTimeout = DateTime.UtcNow;
+ }
+
+ if (statusCode == HttpStatusCode.RequestEntityTooLarge)
if (options.LogErrorResponseBody)
{
try
diff --git a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
index ea4a61e25a..e9ef846634 100644
--- a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
+++ b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
@@ -234,10 +234,10 @@ namespace MediaBrowser.Common.Implementations.IO
{
if (_supportsAsyncFileStreams && isAsync)
{
- return new FileStream(path, mode, access, share, 4096, true);
+ return new FileStream(path, mode, access, share, StreamDefaults.DefaultFileStreamBufferSize, true);
}
- return new FileStream(path, mode, access, share);
+ return new FileStream(path, mode, access, share, StreamDefaults.DefaultFileStreamBufferSize);
}
///
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index c2551731fb..cbd1c1ac55 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -312,7 +312,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
trigger.Triggered -= trigger_Triggered;
trigger.Triggered += trigger_Triggered;
- trigger.Start(isApplicationStartup);
+ trigger.Start(LastExecutionResult, isApplicationStartup);
}
}
@@ -340,7 +340,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
await Task.Delay(1000).ConfigureAwait(false);
- trigger.Start(false);
+ trigger.Start(LastExecutionResult, false);
}
private Task _currentTask;
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index eb2b46c223..d9c178d8b5 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -45,9 +45,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
// Until we can vary these default triggers per server and MBT, we need something that makes sense for both
return new ITaskTrigger[] {
- // At startup
- new StartupTrigger {DelayMs = 60000},
-
// Every so often
new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
};
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 6b9bcbfc1a..b2759c52a6 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -42,9 +42,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
// Until we can vary these default triggers per server and MBT, we need something that makes sense for both
return new ITaskTrigger[] {
- // At startup
- new StartupTrigger {DelayMs = 30000},
-
// Every so often
new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
};
diff --git a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs
index b49551ea99..f194b334a8 100644
--- a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs
+++ b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs
@@ -61,7 +61,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
private Stream OpenFile(string path)
{
- return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
+ return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072);
}
///
diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
index 23785083b6..5f205d69e7 100644
--- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
+++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
@@ -149,10 +149,12 @@ namespace MediaBrowser.Common.Implementations.Updates
/// Gets all available packages.
///
/// The cancellation token.
+ /// if set to true [with registration].
/// Type of the package.
/// The application version.
/// Task{List{PackageInfo}}.
public async Task> GetAvailablePackages(CancellationToken cancellationToken,
+ bool withRegistration = true,
PackageType? packageType = null,
Version applicationVersion = null)
{
@@ -163,13 +165,22 @@ namespace MediaBrowser.Common.Implementations.Updates
{ "systemid", _applicationHost.SystemId }
};
- using (var json = await _httpClient.Post(MbAdmin.HttpsUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+ if (withRegistration)
{
- cancellationToken.ThrowIfCancellationRequested();
+ using (var json = await _httpClient.Post(MbAdmin.HttpsUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
- var packages = _jsonSerializer.DeserializeFromStream>(json).ToList();
+ var packages = _jsonSerializer.DeserializeFromStream>(json).ToList();
+
+ return FilterPackages(packages, packageType, applicationVersion);
+ }
+ }
+ else
+ {
+ var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
- return FilterPackages(packages, packageType, applicationVersion);
+ return FilterPackages(packages.ToList(), packageType, applicationVersion);
}
}
diff --git a/MediaBrowser.Common/IO/StreamDefaults.cs b/MediaBrowser.Common/IO/StreamDefaults.cs
index 0cbf1643fb..450d293d4b 100644
--- a/MediaBrowser.Common/IO/StreamDefaults.cs
+++ b/MediaBrowser.Common/IO/StreamDefaults.cs
@@ -9,11 +9,11 @@ namespace MediaBrowser.Common.IO
///
/// The default copy to buffer size
///
- public const int DefaultCopyToBufferSize = 81920;
+ public const int DefaultCopyToBufferSize = 262144;
///
/// The default file stream buffer size
///
- public const int DefaultFileStreamBufferSize = 4096;
+ public const int DefaultFileStreamBufferSize = 262144;
}
}
diff --git a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs b/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs
index 2f935607bf..382a412552 100644
--- a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs
@@ -1,6 +1,7 @@
-using System;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Tasks;
+using System;
using System.Threading;
-using MediaBrowser.Model.Events;
namespace MediaBrowser.Common.ScheduledTasks
{
@@ -32,8 +33,9 @@ namespace MediaBrowser.Common.ScheduledTasks
///
/// Stars waiting for the trigger action
///
+ /// The last result.
/// if set to true [is application startup].
- public void Start(bool isApplicationStartup)
+ public void Start(TaskResult lastResult, bool isApplicationStartup)
{
DisposeTimer();
diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs b/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs
index d30111316a..8c87f8f380 100644
--- a/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs
@@ -1,5 +1,6 @@
-using System;
-using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Tasks;
+using System;
namespace MediaBrowser.Common.ScheduledTasks
{
@@ -16,8 +17,9 @@ namespace MediaBrowser.Common.ScheduledTasks
///
/// Stars waiting for the trigger action
///
+ /// The last result.
/// if set to true [is application startup].
- void Start(bool isApplicationStartup);
+ void Start(TaskResult lastResult, bool isApplicationStartup);
///
/// Stops waiting for the trigger action
diff --git a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
index 455a70d7e5..b615adf816 100644
--- a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
@@ -1,6 +1,7 @@
-using System;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Tasks;
+using System;
using System.Threading;
-using MediaBrowser.Model.Events;
namespace MediaBrowser.Common.ScheduledTasks
{
@@ -29,15 +30,43 @@ namespace MediaBrowser.Common.ScheduledTasks
///
public TaskExecutionOptions TaskOptions { get; set; }
+ ///
+ /// Gets or sets the first run delay.
+ ///
+ /// The first run delay.
+ public TimeSpan FirstRunDelay { get; set; }
+
+ public IntervalTrigger()
+ {
+ FirstRunDelay = TimeSpan.FromHours(1);
+ }
+
///
/// Stars waiting for the trigger action
///
+ /// The last result.
/// if set to true [is application startup].
- public void Start(bool isApplicationStartup)
+ public void Start(TaskResult lastResult, bool isApplicationStartup)
{
DisposeTimer();
- Timer = new Timer(state => OnTriggered(), null, Interval, TimeSpan.FromMilliseconds(-1));
+ var triggerDate = lastResult != null ?
+ lastResult.EndTimeUtc.Add(Interval) :
+ DateTime.UtcNow.Add(FirstRunDelay);
+
+ if (DateTime.UtcNow > triggerDate)
+ {
+ if (isApplicationStartup)
+ {
+ triggerDate = DateTime.UtcNow.AddMinutes(5);
+ }
+ else
+ {
+ triggerDate = DateTime.UtcNow.Add(Interval);
+ }
+ }
+
+ Timer = new Timer(state => OnTriggered(), null, triggerDate - DateTime.UtcNow, TimeSpan.FromMilliseconds(-1));
}
///
diff --git a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs
index a58fa22b9b..1d82dc76a5 100644
--- a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs
@@ -1,6 +1,7 @@
-using System;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Tasks;
+using System;
using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
namespace MediaBrowser.Common.ScheduledTasks
{
@@ -27,8 +28,9 @@ namespace MediaBrowser.Common.ScheduledTasks
///
/// Stars waiting for the trigger action
///
+ /// The last result.
/// if set to true [is application startup].
- public async void Start(bool isApplicationStartup)
+ public async void Start(TaskResult lastResult, bool isApplicationStartup)
{
if (isApplicationStartup)
{
diff --git a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs b/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs
index a40dc6b5cb..eaf4afc758 100644
--- a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Tasks;
using Microsoft.Win32;
using System;
using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
namespace MediaBrowser.Common.ScheduledTasks
{
@@ -28,8 +28,9 @@ namespace MediaBrowser.Common.ScheduledTasks
///
/// Stars waiting for the trigger action
///
+ /// The last result.
/// if set to true [is application startup].
- public void Start(bool isApplicationStartup)
+ public void Start(TaskResult lastResult, bool isApplicationStartup)
{
switch (SystemEvent)
{
diff --git a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs b/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs
index a3818f83fd..2e38264b24 100644
--- a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs
+++ b/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs
@@ -1,6 +1,7 @@
using System;
using System.Threading;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Tasks;
namespace MediaBrowser.Common.ScheduledTasks
{
@@ -38,8 +39,9 @@ namespace MediaBrowser.Common.ScheduledTasks
///
/// Stars waiting for the trigger action
///
+ /// The last result.
/// if set to true [is application startup].
- public void Start(bool isApplicationStartup)
+ public void Start(TaskResult lastResult, bool isApplicationStartup)
{
DisposeTimer();
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index 592613c54c..7d721da6f0 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -45,10 +45,12 @@ namespace MediaBrowser.Common.Updates
/// Gets all available packages.
///
/// The cancellation token.
+ /// if set to true [with registration].
/// Type of the package.
/// The application version.
/// Task{List{PackageInfo}}.
Task> GetAvailablePackages(CancellationToken cancellationToken,
+ bool withRegistration = true,
PackageType? packageType = null,
Version applicationVersion = null);
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index f5c4ab3733..8d3e0f5962 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -121,10 +121,9 @@ namespace MediaBrowser.Controller.Channels
///
/// Gets the channel folder.
///
- /// The user identifier.
/// The cancellation token.
/// BaseItemDto.
- Task GetInternalChannelFolder(string userId, CancellationToken cancellationToken);
+ Task GetInternalChannelFolder(CancellationToken cancellationToken);
///
/// Gets the channel folder.
diff --git a/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
new file mode 100644
index 0000000000..e8083b3632
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace MediaBrowser.Controller.Dlna
+{
+ public interface IDeviceDiscovery
+ {
+ event EventHandler DeviceDiscovered;
+ event EventHandler DeviceLeft;
+ }
+}
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index 5ec8f274be..e4ab291025 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -81,13 +81,11 @@ namespace MediaBrowser.Controller.Dto
///
/// Gets the item by name dto.
///
- ///
/// The item.
/// The options.
/// The tagged items.
/// The user.
/// BaseItemDto.
- BaseItemDto GetItemByNameDto(T item, DtoOptions options, List taggedItems, User user = null)
- where T : BaseItem, IItemByName;
+ BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List taggedItems, User user = null);
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 8a77d76165..807beee523 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -54,6 +54,12 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return AlbumArtists.FirstOrDefault(); }
}
+ [IgnoreDataMember]
+ public override bool SupportsPeople
+ {
+ get { return false; }
+ }
+
public List AlbumArtists { get; set; }
///
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index d9dbf265fc..594b5ca93c 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -142,7 +142,7 @@ namespace MediaBrowser.Controller.Entities
public virtual string Path { get; set; }
[IgnoreDataMember]
- protected internal bool IsOffline { get; set; }
+ public bool IsOffline { get; set; }
///
/// Returns the folder containing the item.
@@ -419,6 +419,10 @@ namespace MediaBrowser.Controller.Entities
return _sortName ?? (_sortName = CreateSortName());
}
+ set
+ {
+ _sortName = value;
+ }
}
public string GetInternalMetadataPath()
@@ -485,6 +489,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the parent.
///
/// The parent.
+ [IgnoreDataMember]
public Folder Parent
{
get
@@ -1115,6 +1120,23 @@ namespace MediaBrowser.Controller.Entities
return value.Value <= maxAllowedRating.Value;
}
+ public int? GetParentalRatingValue()
+ {
+ var rating = CustomRatingForComparison;
+
+ if (string.IsNullOrWhiteSpace(rating))
+ {
+ rating = OfficialRatingForComparison;
+ }
+
+ if (string.IsNullOrWhiteSpace(rating))
+ {
+ return null;
+ }
+
+ return LocalizationManager.GetRatingLevel(rating);
+ }
+
private bool IsVisibleViaTags(User user)
{
var hasTags = this as IHasTags;
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 3a610be641..8821f35c8b 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -35,6 +35,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ protected override bool SupportsShortcutChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
+
public override bool CanDelete()
{
return false;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 22efb09e15..c3ac77328d 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -14,7 +13,6 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -50,7 +48,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public virtual bool IsPreSorted
{
- get { return false; }
+ get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
}
///
@@ -122,7 +120,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
protected virtual bool SupportsShortcutChildren
{
- get { return true; }
+ get { return false; }
}
///
@@ -176,7 +174,7 @@ namespace MediaBrowser.Controller.Entities
protected void AddChildInternal(BaseItem child)
{
var actualChildren = ActualChildren;
-
+
lock (_childrenSyncLock)
{
var newChildren = actualChildren.ToList();
@@ -1070,7 +1068,7 @@ namespace MediaBrowser.Controller.Entities
{
var changesFound = false;
- if (SupportsShortcutChildren && LocationType == LocationType.FileSystem)
+ if (LocationType == LocationType.FileSystem)
{
if (RefreshLinkedChildren(fileSystemChildren))
{
@@ -1092,37 +1090,43 @@ namespace MediaBrowser.Controller.Entities
var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList();
var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
- var newShortcutLinks = fileSystemChildren
- .Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory && FileSystem.IsShortcut(i.FullName))
- .Select(i =>
- {
- try
+ List newShortcutLinks;
+
+ if (SupportsShortcutChildren)
+ {
+ newShortcutLinks = fileSystemChildren
+ .Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory && FileSystem.IsShortcut(i.FullName))
+ .Select(i =>
{
- Logger.Debug("Found shortcut at {0}", i.FullName);
+ try
+ {
+ Logger.Debug("Found shortcut at {0}", i.FullName);
- var resolvedPath = FileSystem.ResolveShortcut(i.FullName);
+ var resolvedPath = FileSystem.ResolveShortcut(i.FullName);
- if (!string.IsNullOrEmpty(resolvedPath))
- {
- return new LinkedChild
+ if (!string.IsNullOrEmpty(resolvedPath))
{
- Path = resolvedPath,
- Type = LinkedChildType.Shortcut
- };
- }
+ return new LinkedChild
+ {
+ Path = resolvedPath,
+ Type = LinkedChildType.Shortcut
+ };
+ }
- Logger.Error("Error resolving shortcut {0}", i.FullName);
+ Logger.Error("Error resolving shortcut {0}", i.FullName);
- return null;
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error resolving shortcut {0}", ex, i.FullName);
- return null;
- }
- })
- .Where(i => i != null)
- .ToList();
+ return null;
+ }
+ catch (IOException ex)
+ {
+ Logger.ErrorException("Error resolving shortcut {0}", ex, i.FullName);
+ return null;
+ }
+ })
+ .Where(i => i != null)
+ .ToList();
+ }
+ else { newShortcutLinks = new List(); }
if (!newShortcutLinks.SequenceEqual(currentShortcutLinks, new LinkedChildComparer()))
{
diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs
index f46d7ed6f3..b55ca0a179 100644
--- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/ICollectionFolder.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace MediaBrowser.Controller.Entities
{
@@ -14,4 +15,17 @@ namespace MediaBrowser.Controller.Entities
Guid Id { get; }
IEnumerable PhysicalLocations { get; }
}
+
+ public static class CollectionFolderExtensions
+ {
+ public static string GetViewType(this ICollectionFolder folder, User user)
+ {
+ if (user.Configuration.PlainFolderViews.Contains(folder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ return folder.CollectionType;
+ }
+ }
}
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/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index c5e60a73d9..0af4972f74 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -1,6 +1,6 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@@ -40,6 +40,7 @@ namespace MediaBrowser.Controller.Entities
public string NameStartsWithOrGreater { get; set; }
public string NameStartsWith { get; set; }
public string NameLessThan { get; set; }
+ public string NameContains { get; set; }
public string Person { get; set; }
public string[] PersonIds { get; set; }
@@ -93,7 +94,11 @@ namespace MediaBrowser.Controller.Entities
public string[] ChannelIds { get; set; }
internal List ItemIdsFromPersonFilters { get; set; }
+ public int? MaxParentalRating { get; set; }
+ public bool? IsCurrentSchema { get; set; }
+ public bool? HasDeadParentId { get; set; }
+
public InternalItemsQuery()
{
Tags = new string[] { };
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 02e9d4cf9e..9317f688fe 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -74,6 +74,15 @@ namespace MediaBrowser.Controller.Entities.Movies
}
}
+ [IgnoreDataMember]
+ protected override bool SupportsShortcutChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
+
public override bool IsAuthorizedToDelete(User user)
{
return true;
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index fc07f67788..083ec0cb4c 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -100,16 +100,23 @@ namespace MediaBrowser.Controller.Entities.Movies
/// System.String.
protected override string CreateUserDataKey()
{
- var key = this.GetProviderId(MetadataProviders.Tmdb);
+ var key = GetMovieUserDataKey(this);
if (string.IsNullOrWhiteSpace(key))
{
- key = this.GetProviderId(MetadataProviders.Imdb);
+ key = base.CreateUserDataKey();
}
+ return key;
+ }
+
+ public static string GetMovieUserDataKey(BaseItem movie)
+ {
+ var key = movie.GetProviderId(MetadataProviders.Tmdb);
+
if (string.IsNullOrWhiteSpace(key))
{
- key = base.CreateUserDataKey();
+ key = movie.GetProviderId(MetadataProviders.Imdb);
}
return key;
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 8f5b8f6cff..5163c3de4a 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -150,7 +150,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
var series = Series;
- if (ParentIndexNumber.HasValue)
+ if (series != null && ParentIndexNumber.HasValue)
{
var findNumber = ParentIndexNumber.Value;
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index b065ae1715..a78beb645d 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Providers;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
@@ -36,6 +37,16 @@ namespace MediaBrowser.Controller.Entities
return PostFilterAndSort(result.Where(filter), query);
}
+ [IgnoreDataMember]
+ protected override bool SupportsShortcutChildren
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ [IgnoreDataMember]
public override bool IsPreSorted
{
get
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index dad6de01a8..488e54cc3c 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -1,10 +1,10 @@
-using System.Runtime.Serialization;
-using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
+using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Entities
public Guid ParentId { get; set; }
public Guid? UserId { get; set; }
-
+
public static ITVSeriesManager TVSeriesManager;
public static IPlaylistManager PlaylistManager;
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index b2d3592776..cee5dadd25 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -66,7 +66,7 @@ namespace MediaBrowser.Controller.Entities
{
var result = await _channelManager.GetChannelsInternal(new ChannelQuery
{
- UserId = user.Id.ToString("N"),
+ UserId = user == null ? null : user.Id.ToString("N"),
Limit = query.Limit,
StartIndex = query.StartIndex
@@ -264,10 +264,7 @@ namespace MediaBrowser.Controller.Entities
private async Task> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
{
- var collectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList();
-
- var list = _playlistManager.GetPlaylists(user.Id.ToString("N"))
- .Where(i => i.GetChildren(user, true).Any(media => _libraryManager.GetCollectionFolders(media).Select(c => c.Id).Any(collectionFolders.Contains)));
+ var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
return GetResult(list, parent, query);
}
@@ -288,14 +285,14 @@ namespace MediaBrowser.Controller.Entities
var list = new List();
- list.Add(await GetUserView(SpecialFolder.MusicLatest, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicPlaylists, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "3", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicLatest, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicPlaylists, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicAlbums, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, "3", parent).ConfigureAwait(false));
//list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "5", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "6", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "7", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicSongs, "5", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicGenres, "6", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavorites, "7", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -304,9 +301,9 @@ namespace MediaBrowser.Controller.Entities
{
var list = new List();
- list.Add(await GetUserView(SpecialFolder.MusicFavoriteAlbums, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicFavoriteArtists, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MusicFavoriteSongs, user, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavoriteAlbums, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavoriteArtists, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MusicFavoriteSongs, "2", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -332,7 +329,7 @@ namespace MediaBrowser.Controller.Entities
})
.Where(i => i != null)
- .Select(i => GetUserView(i.Name, SpecialFolder.MusicGenre, user, i.SortName, parent));
+ .Select(i => GetUserView(i.Name, SpecialFolder.MusicGenre, i.SortName, parent));
var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -344,94 +341,42 @@ namespace MediaBrowser.Controller.Entities
var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
.Where(i => !i.IsFolder)
.Where(i => i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase))
- .OfType()
- .SelectMany(i => i.AlbumArtists)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- return _libraryManager.GetArtist(i);
- }
- catch
- {
- // Already logged at lower levels
- return null;
- }
- })
- .Where(i => i != null);
+ .OfType();
- return GetResult(items, queryParent, query);
+ var artists = _libraryManager.GetAlbumArtists(items);
+
+ return GetResult(artists, queryParent, query);
}
private QueryResult GetMusicAlbumArtists(Folder parent, User user, InternalItemsQuery query)
{
- var artists = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
.Where(i => !i.IsFolder)
- .OfType()
- .SelectMany(i => i.AlbumArtists)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- return _libraryManager.GetArtist(i);
- }
- catch
- {
- // Already logged at lower levels
- return null;
- }
- })
- .Where(i => i != null);
+ .OfType();
+
+ var artists = _libraryManager.GetAlbumArtists(items);
return GetResult(artists, parent, query);
}
private QueryResult GetMusicArtists(Folder parent, User user, InternalItemsQuery query)
{
- var artists = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
.Where(i => !i.IsFolder)
- .OfType()
- .SelectMany(i => i.Artists)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- return _libraryManager.GetArtist(i);
- }
- catch
- {
- // Already logged at lower levels
- return null;
- }
- })
- .Where(i => i != null);
+ .OfType();
+
+ var artists = _libraryManager.GetArtists(items);
return GetResult(artists, parent, query);
}
private QueryResult GetFavoriteArtists(Folder parent, User user, InternalItemsQuery query)
{
- var artists = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos })
.Where(i => !i.IsFolder)
- .OfType()
- .SelectMany(i => i.AlbumArtists)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- return _libraryManager.GetArtist(i);
- }
- catch
- {
- // Already logged at lower levels
- return null;
- }
- })
- .Where(i => i != null && _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite);
+ .OfType();
+
+ var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite);
return GetResult(artists, parent, query);
}
@@ -498,12 +443,12 @@ namespace MediaBrowser.Controller.Entities
var list = new List();
- list.Add(await GetUserView(SpecialFolder.MovieResume, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MovieLatest, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MovieMovies, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MovieCollections, user, "3", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MovieFavorites, user, "4", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.MovieGenres, user, "5", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieResume, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieLatest, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieMovies, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieCollections, "3", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieFavorites, "4", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.MovieGenres, "5", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -609,7 +554,7 @@ namespace MediaBrowser.Controller.Entities
})
.Where(i => i != null)
- .Select(i => GetUserView(i.Name, SpecialFolder.MovieGenre, user, i.SortName, parent));
+ .Select(i => GetUserView(i.Name, SpecialFolder.MovieGenre, i.SortName, parent));
var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -671,13 +616,13 @@ namespace MediaBrowser.Controller.Entities
var list = new List();
- list.Add(await GetUserView(SpecialFolder.TvResume, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.TvNextUp, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.TvLatest, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.TvShowSeries, user, "3", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.TvFavoriteSeries, user, "4", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.TvFavoriteEpisodes, user, "5", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.TvGenres, user, "6", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvResume, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvNextUp, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvLatest, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvShowSeries, "3", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvFavoriteSeries, "4", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvFavoriteEpisodes, "5", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.TvGenres, "6", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -692,11 +637,11 @@ namespace MediaBrowser.Controller.Entities
var list = new List();
- list.Add(await GetUserView(SpecialFolder.LatestGames, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.GameFavorites, user, "2", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.GameSystems, user, "3", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.GameGenres, user, "4", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.LatestGames, "0", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, "1", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.GameFavorites, "2", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.GameSystems, "3", parent).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.GameGenres, "4", parent).ConfigureAwait(false));
return GetResult(list, parent, query);
}
@@ -794,7 +739,7 @@ namespace MediaBrowser.Controller.Entities
})
.Where(i => i != null)
- .Select(i => GetUserView(i.Name, SpecialFolder.TvGenre, user, i.SortName, parent));
+ .Select(i => GetUserView(i.Name, SpecialFolder.TvGenre, i.SortName, parent));
var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -846,7 +791,7 @@ namespace MediaBrowser.Controller.Entities
})
.Where(i => i != null)
- .Select(i => GetUserView(i.Name, SpecialFolder.GameGenre, user, i.SortName, parent));
+ .Select(i => GetUserView(i.Name, SpecialFolder.GameGenre, i.SortName, parent));
var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -1926,26 +1871,20 @@ namespace MediaBrowser.Controller.Entities
var list = new List();
//list.Add(await GetUserSubView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, user.RootFolder).ConfigureAwait(false));
- list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, user.RootFolder).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.LiveTvChannels, string.Empty, user.RootFolder).ConfigureAwait(false));
+ list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, string.Empty, user.RootFolder).ConfigureAwait(false));
return GetResult(list, queryParent, query);
}
- private async Task GetUserView(string name, string type, User user, string sortName, BaseItem parent)
+ private Task GetUserView(string name, string type, string sortName, BaseItem parent)
{
- var view = await _userViewManager.GetUserSubView(name, parent.Id.ToString("N"), type, user, sortName, CancellationToken.None)
- .ConfigureAwait(false);
-
- return view;
+ return _userViewManager.GetUserSubView(name, parent.Id.ToString("N"), type, sortName, CancellationToken.None);
}
- private async Task GetUserView(string type, User user, string sortName, BaseItem parent)
+ private Task GetUserView(string type, string sortName, BaseItem parent)
{
- var view = await _userViewManager.GetUserSubView(parent.Id.ToString("N"), type, user, sortName, CancellationToken.None)
- .ConfigureAwait(false);
-
- return view;
+ return _userViewManager.GetUserSubView(parent.Id.ToString("N"), type, sortName, CancellationToken.None);
}
public static bool IsYearMismatched(BaseItem item, ILibraryManager libraryManager)
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index f0bfaaf667..9331ca7598 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -5,12 +5,12 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Library
{
@@ -61,7 +61,18 @@ namespace MediaBrowser.Controller.Library
/// The name.
/// Task{Artist}.
MusicArtist GetArtist(string name);
-
+ ///
+ /// Gets the album artists.
+ ///
+ /// The items.
+ /// IEnumerable<MusicArtist>.
+ IEnumerable GetAlbumArtists(IEnumerable items);
+ ///
+ /// Gets the artists.
+ ///
+ /// The items.
+ /// IEnumerable<MusicArtist>.
+ IEnumerable GetArtists(IEnumerable items);
///
/// Gets a Studio
///
@@ -340,7 +351,37 @@ namespace MediaBrowser.Controller.Library
Task GetNamedView(User user,
string name,
string viewType,
- string sortName,
+ string sortName,
+ CancellationToken cancellationToken);
+
+ ///
+ /// Gets the named view.
+ ///
+ /// The name.
+ /// Type of the view.
+ /// Name of the sort.
+ /// The cancellation token.
+ /// Task<UserView>.
+ Task GetNamedView(string name,
+ string viewType,
+ string sortName,
+ CancellationToken cancellationToken);
+
+ ///
+ /// Gets the named view.
+ ///
+ /// The name.
+ /// The parent identifier.
+ /// Type of the view.
+ /// Name of the sort.
+ /// The unique identifier.
+ /// The cancellation token.
+ /// Task<UserView>.
+ Task GetNamedView(string name,
+ string parentId,
+ string viewType,
+ string sortName,
+ string uniqueId,
CancellationToken cancellationToken);
///
@@ -461,5 +502,12 @@ namespace MediaBrowser.Controller.Library
/// The query.
/// List<System.String>.
List GetPeopleNames(InternalPeopleQuery query);
+
+ ///
+ /// Queries the items.
+ ///
+ /// The query.
+ /// QueryResult<BaseItem>.
+ QueryResult QueryItems(InternalItemsQuery query);
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
index 0883da48f8..e09e583024 100644
--- a/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
+++ b/MediaBrowser.Controller/Library/IMetadataFileSaver.cs
@@ -11,4 +11,9 @@ namespace MediaBrowser.Controller.Library
/// System.String.
string GetSavePath(IHasMetadata item);
}
+
+ public interface IConfigurableProvider
+ {
+ bool IsEnabled { get; }
+ }
}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index 0ce0687cc1..9baf8b6f12 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -16,10 +16,10 @@ namespace MediaBrowser.Controller.Library
///
/// Gets the instant mix from artist.
///
- /// The name.
+ /// The artist.
/// The user.
/// IEnumerable{Audio}.
- IEnumerable
/// null if [has image] contains no value, true if [has image]; otherwise, false.
public bool? HasImage { get; set; }
-
+ ///
+ /// Gets or sets a value indicating whether this instance is favorite.
+ ///
+ /// null if [is favorite] contains no value, true if [is favorite]; otherwise, false.
+ public bool? IsFavorite { get; set; }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs b/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs
new file mode 100644
index 0000000000..3626c18e54
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/IHasRegistrationInfo.cs
@@ -0,0 +1,15 @@
+using MediaBrowser.Model.Entities;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.LiveTv
+{
+ public interface IHasRegistrationInfo
+ {
+ ///
+ /// Gets the registration information.
+ ///
+ /// The feature.
+ /// Task<MBRegistrationRecord>.
+ Task GetRegistrationInfo(string feature);
+ }
+}
diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
new file mode 100644
index 0000000000..e60183bd93
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs
@@ -0,0 +1,19 @@
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.LiveTv
+{
+ public interface IListingsProvider
+ {
+ string Name { get; }
+ string Type { get; }
+ Task> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
+ Task AddMetadata(ListingsProviderInfo info, List channels, CancellationToken cancellationToken);
+ Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings);
+ Task> GetLineups(ListingsProviderInfo info, string country, string location);
+ }
+}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 3aa1f66efb..1458c1bc2e 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using System.Collections.Generic;
@@ -56,20 +57,23 @@ namespace MediaBrowser.Controller.LiveTv
/// The identifier.
/// Task.
Task CancelSeriesTimer(string id);
-
+
///
/// Adds the parts.
///
/// The services.
- void AddParts(IEnumerable services);
+ /// The tuner hosts.
+ /// The listing providers.
+ void AddParts(IEnumerable services, IEnumerable tunerHosts, IEnumerable listingProviders);
///
/// Gets the channels.
///
/// The query.
+ /// The options.
/// The cancellation token.
/// IEnumerable{Channel}.
- Task> GetChannels(LiveTvChannelQuery query, CancellationToken cancellationToken);
+ Task> GetChannels(LiveTvChannelQuery query, DtoOptions options, CancellationToken cancellationToken);
///
/// Gets the recording.
@@ -171,14 +175,15 @@ namespace MediaBrowser.Controller.LiveTv
/// The user.
/// Task{ProgramInfoDto}.
Task GetProgram(string id, CancellationToken cancellationToken, User user = null);
-
+
///
/// Gets the programs.
///
/// The query.
+ /// The options.
/// The cancellation token.
/// IEnumerable{ProgramInfo}.
- Task> GetPrograms(ProgramQuery query, CancellationToken cancellationToken);
+ Task> GetPrograms(ProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
///
/// Updates the timer.
@@ -238,10 +243,10 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the recommended programs.
///
/// The query.
+ /// The options.
/// The cancellation token.
/// Task{QueryResult{ProgramInfoDto}}.
- Task> GetRecommendedPrograms(RecommendedProgramQuery query,
- CancellationToken cancellationToken);
+ Task> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
///
/// Gets the recommended programs internal.
@@ -249,8 +254,7 @@ namespace MediaBrowser.Controller.LiveTv
/// The query.
/// The cancellation token.
/// Task<QueryResult<LiveTvProgram>>.
- Task> GetRecommendedProgramsInternal(RecommendedProgramQuery query,
- CancellationToken cancellationToken);
+ Task> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken);
///
/// Gets the live tv information.
@@ -270,10 +274,9 @@ namespace MediaBrowser.Controller.LiveTv
///
/// Gets the live tv folder.
///
- /// The user identifier.
/// The cancellation token.
/// BaseItemDto.
- Task GetInternalLiveTvFolder(string userId, CancellationToken cancellationToken);
+ Task GetInternalLiveTvFolder(CancellationToken cancellationToken);
///
/// Gets the live tv folder.
@@ -337,5 +340,46 @@ namespace MediaBrowser.Controller.LiveTv
/// The dto.
/// The user.
void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, User user = null);
+ ///
+ /// Saves the tuner host.
+ ///
+ /// The information.
+ /// Task.
+ Task SaveTunerHost(TunerHostInfo info);
+ ///
+ /// Saves the listing provider.
+ ///
+ /// The information.
+ /// if set to true [validate login].
+ /// if set to true [validate listings].
+ /// Task.
+ Task SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings);
+ ///
+ /// Gets the lineups.
+ ///
+ /// Type of the provider.
+ /// The provider identifier.
+ /// The country.
+ /// The location.
+ /// Task<List<NameIdPair>>.
+ Task> GetLineups(string providerType, string providerId, string country, string location);
+
+ ///
+ /// Gets the registration information.
+ ///
+ /// The channel identifier.
+ /// The program identifier.
+ /// The feature.
+ /// Task<MBRegistrationRecord>.
+ Task GetRegistrationInfo(string channelId, string programId, string feature);
+
+ ///
+ /// Adds the channel information.
+ ///
+ /// The dto.
+ /// The channel.
+ /// The options.
+ /// The user.
+ void AddChannelInfo(BaseItemDto dto, LiveTvChannel channel, DtoOptions options, User user);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
index 1dd267c939..ba0b82a0b6 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
@@ -37,10 +37,12 @@ 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; }
ChannelType ChannelType { get; set; }
+ DateTime DateLastSaved { get; set; }
+ DateTime DateCreated { get; set; }
+ DateTime DateModified { get; set; }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
new file mode 100644
index 0000000000..bedbcffe32
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -0,0 +1,55 @@
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.LiveTv
+{
+ public interface ITunerHost
+ {
+ ///
+ /// Gets the name.
+ ///
+ /// The name.
+ string Name { get; }
+ ///
+ /// Gets the type.
+ ///
+ /// The type.
+ string Type { get; }
+ ///
+ /// Gets the channels.
+ ///
+ /// The cancellation token.
+ /// Task<IEnumerable<ChannelInfo>>.
+ Task> GetChannels(CancellationToken cancellationToken);
+ ///
+ /// Gets the tuner infos.
+ ///
+ /// The cancellation token.
+ /// Task<List<LiveTvTunerInfo>>.
+ Task> GetTunerInfos(CancellationToken cancellationToken);
+ ///
+ /// Gets the channel stream.
+ ///
+ /// The channel identifier.
+ /// The stream identifier.
+ /// The cancellation token.
+ /// Task<MediaSourceInfo>.
+ Task GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
+ ///
+ /// Gets the channel stream media sources.
+ ///
+ /// The channel identifier.
+ /// The cancellation token.
+ /// Task<List<MediaSourceInfo>>.
+ Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
+ ///
+ /// Validates the specified information.
+ ///
+ /// The information.
+ /// Task.
+ Task Validate(TunerHostInfo info);
+ }
+}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 8232c5c7ad..12052905f2 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
@@ -17,9 +18,24 @@ namespace MediaBrowser.Controller.LiveTv
/// System.String.
protected override string CreateUserDataKey()
{
+ if (IsMovie)
+ {
+ var key = Movie.GetMovieUserDataKey(this);
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ return key;
+ }
+ }
return GetClientTypeName() + "-" + Name;
}
+ ///
+ /// Gets or sets the etag.
+ ///
+ /// The etag.
+ public string Etag { get; set; }
+
///
/// Id of the program.
///
@@ -227,5 +243,19 @@ namespace MediaBrowser.Controller.LiveTv
info.IsMovie = IsMovie;
return info;
}
+
+ public override bool SupportsPeople
+ {
+ get
+ {
+ // Optimization
+ if (IsNews || IsSports)
+ {
+ return false;
+ }
+
+ return base.SupportsPeople;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
index 0cb064aba3..4da238acf5 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
@@ -34,10 +34,16 @@ namespace MediaBrowser.Controller.LiveTv
///
/// The tuners.
public List Tuners { get; set; }
-
+ ///
+ /// Gets or sets a value indicating whether this instance is visible.
+ ///
+ /// true if this instance is visible; otherwise, false.
+ public bool IsVisible { get; set; }
+
public LiveTvServiceStatusInfo()
{
Tuners = new List();
+ IsVisible = true;
}
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index aaaff6bdb9..960f8054a1 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -40,6 +41,16 @@ namespace MediaBrowser.Controller.LiveTv
/// System.String.
protected override string CreateUserDataKey()
{
+ if (IsMovie)
+ {
+ var key = Movie.GetMovieUserDataKey(this);
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ return key;
+ }
+ }
+
var name = GetClientTypeName();
if (!string.IsNullOrEmpty(ProgramId))
diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
index 36f082b02c..a6a3e61081 100644
--- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
@@ -33,6 +33,11 @@ namespace MediaBrowser.Controller.LiveTv
///
/// The overview.
public string Overview { get; set; }
+ ///
+ /// Gets or sets the short overview.
+ ///
+ /// The short overview.
+ public string ShortOverview { get; set; }
///
/// The start date of the program, in UTC.
@@ -150,6 +155,36 @@ namespace MediaBrowser.Controller.LiveTv
///
/// The production year.
public int? ProductionYear { get; set; }
+ ///
+ /// Gets or sets the home page URL.
+ ///
+ /// The home page URL.
+ public string HomePageUrl { get; set; }
+ ///
+ /// Gets or sets the series identifier.
+ ///
+ /// The series identifier.
+ public string SeriesId { get; set; }
+ ///
+ /// Gets or sets the show identifier.
+ ///
+ /// The show identifier.
+ public string ShowId { get; set; }
+ ///
+ /// Gets or sets the season number.
+ ///
+ /// The season number.
+ public int? SeasonNumber { get; set; }
+ ///
+ /// Gets or sets the episode number.
+ ///
+ /// The episode number.
+ public int? EpisodeNumber { get; set; }
+ ///
+ /// Gets or sets the etag.
+ ///
+ /// The etag.
+ public string Etag { get; set; }
public ProgramInfo()
{
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
index bf453ccf4d..3006b9bbef 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
@@ -16,6 +16,12 @@ namespace MediaBrowser.Controller.LiveTv
///
/// The series timer identifier.
public string SeriesTimerId { get; set; }
+
+ ///
+ /// Gets or sets the timer identifier.
+ ///
+ /// The timer identifier.
+ public string TimerId { get; set; }
///
/// ChannelId of the recording.
@@ -179,7 +185,17 @@ namespace MediaBrowser.Controller.LiveTv
///
/// null if [has image] contains no value, true if [has image]; otherwise, false.
public bool? HasImage { get; set; }
+ ///
+ /// Gets or sets the show identifier.
+ ///
+ /// The show identifier.
+ public string ShowId { get; set; }
+ ///
+ /// Gets or sets the date last updated.
+ ///
+ /// The date last updated.
+ public DateTime DateLastUpdated { get; set; }
public RecordingInfo()
{
diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
index 1be6549ff8..2d79473f00 100644
--- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
@@ -94,6 +94,12 @@ namespace MediaBrowser.Controller.LiveTv
///
/// true if this instance is post padding required; otherwise, false.
public bool IsPostPaddingRequired { get; set; }
+
+ ///
+ /// Gets or sets the series identifier.
+ ///
+ /// The series identifier.
+ public string SeriesId { get; set; }
public SeriesTimerInfo()
{
diff --git a/MediaBrowser.Controller/Localization/ILocalizationManager.cs b/MediaBrowser.Controller/Localization/ILocalizationManager.cs
index f41940ed45..5742d235cd 100644
--- a/MediaBrowser.Controller/Localization/ILocalizationManager.cs
+++ b/MediaBrowser.Controller/Localization/ILocalizationManager.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
-using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Localization
@@ -47,26 +46,10 @@ namespace MediaBrowser.Controller.Localization
/// System.String.
string GetLocalizedString(string phrase);
- ///
- /// Localizes the document.
- ///
- /// The document.
- /// The culture.
- /// The token builder.
- /// System.String.
- string LocalizeDocument(string document, string culture, Func tokenBuilder);
-
///
/// Gets the localization options.
///
/// IEnumerable{LocalizatonOption}.
IEnumerable GetLocalizationOptions();
-
- ///
- /// Gets the java script localization dictionary.
- ///
- /// The culture.
- /// Dictionary{System.StringSystem.String}.
- Dictionary GetJavaScriptLocalizationDictionary(string culture);
}
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index fcde6d8c0e..24309734f8 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -113,6 +113,7 @@
+
@@ -198,7 +199,10 @@
+
+
+
@@ -309,7 +313,6 @@
-
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 5bec7980aa..23285b612a 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -116,5 +116,12 @@ namespace MediaBrowser.Controller.MediaEncoding
Task EncodeVideo(EncodingJobOptions options,
IProgress progress,
CancellationToken cancellationToken);
+
+ ///
+ /// Escapes the subtitle filter path.
+ ///
+ /// The path.
+ /// System.String.
+ string EscapeSubtitleFilterPath(string path);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
index e4a2cd007b..826711e51d 100644
--- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
@@ -7,24 +7,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
public interface ISubtitleEncoder
{
- ///
- /// Converts the subtitles.
- ///
- /// The stream.
- /// The input format.
- /// The output format.
- /// The start time ticks.
- /// The end time ticks.
- /// The cancellation token.
- /// Task{Stream}.
- Task ConvertSubtitles(
- Stream stream,
- string inputFormat,
- string outputFormat,
- long startTimeTicks,
- long? endTimeTicks,
- CancellationToken cancellationToken);
-
///
/// Gets the subtitles.
///
diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
index c1a4fa765a..383d0881e9 100644
--- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
+++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
@@ -74,6 +74,8 @@ namespace MediaBrowser.Controller.Providers
/// The cancellation token.
private void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken)
{
+ item.ResetPeople();
+
using (var streamReader = new StreamReader(metadataFile, encoding))
{
// Use XmlReader for best performance
@@ -492,7 +494,7 @@ namespace MediaBrowser.Controller.Providers
{
continue;
}
- PeopleHelper.AddPerson(itemResult.People, p);
+ itemResult.AddPerson(p);
}
break;
}
@@ -504,7 +506,7 @@ namespace MediaBrowser.Controller.Providers
{
continue;
}
- PeopleHelper.AddPerson(itemResult.People, p);
+ itemResult.AddPerson(p);
}
break;
}
@@ -529,7 +531,7 @@ namespace MediaBrowser.Controller.Providers
{
continue;
}
- PeopleHelper.AddPerson(itemResult.People, p);
+ itemResult.AddPerson(p);
}
}
break;
@@ -543,7 +545,7 @@ namespace MediaBrowser.Controller.Providers
{
continue;
}
- PeopleHelper.AddPerson(itemResult.People, p);
+ itemResult.AddPerson(p);
}
break;
}
@@ -1156,7 +1158,7 @@ namespace MediaBrowser.Controller.Providers
{
continue;
}
- PeopleHelper.AddPerson(item.People, person);
+ item.AddPerson(person);
}
}
break;
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index 9e549dcf3f..79ffb0d01f 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -58,8 +58,9 @@ namespace MediaBrowser.Controller.Providers
try
{
- var list = new DirectoryInfo(path)
- .EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly);
+ // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
+ var list = new DirectoryInfo(path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
+ .Concat(new DirectoryInfo(path).EnumerateFiles("*", SearchOption.TopDirectoryOnly));
// Seeing dupes on some users file system for some reason
foreach (var item in list)
diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
index 3a8ef73254..28a5353104 100644
--- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Providers
/// The directory service.
/// The cancellation token.
/// Task{MetadataResult{`0}}.
- Task> GetMetadata(ItemInfo info,
+ Task> GetMetadata(ItemInfo info,
IDirectoryService directoryService,
CancellationToken cancellationToken);
}
diff --git a/MediaBrowser.Controller/Providers/LocalMetadataResult.cs b/MediaBrowser.Controller/Providers/LocalMetadataResult.cs
deleted file mode 100644
index 76b7a31360..0000000000
--- a/MediaBrowser.Controller/Providers/LocalMetadataResult.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Providers
-{
- public class LocalMetadataResult : MetadataResult
- where T : IHasMetadata
- {
- public List Images { get; set; }
- public List UserDataLIst { get; set; }
-
- public LocalMetadataResult()
- {
- Images = new List();
- UserDataLIst = new List();
- }
- }
-}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs
index a18dd83e83..17175f91cf 100644
--- a/MediaBrowser.Controller/Providers/MetadataResult.cs
+++ b/MediaBrowser.Controller/Providers/MetadataResult.cs
@@ -1,18 +1,67 @@
using MediaBrowser.Controller.Entities;
+using System;
using System.Collections.Generic;
+using System.Linq;
namespace MediaBrowser.Controller.Providers
{
public class MetadataResult
{
+ public List Images { get; set; }
+ public List UserDataList { get; set; }
+
+ public MetadataResult()
+ {
+ Images = new List();
+ }
+
public List People { get; set; }
public bool HasMetadata { get; set; }
public T Item { get; set; }
- public MetadataResult()
+ public void AddPerson(PersonInfo p)
{
- People = new List();
+ if (People == null)
+ {
+ People = new List();
+ }
+
+ PeopleHelper.AddPerson(People, p);
+ }
+
+ ///
+ /// Not only does this clear, but initializes the list so that services can differentiate between a null list and zero people
+ ///
+ public void ResetPeople()
+ {
+ if (People == null)
+ {
+ People = new List();
+ }
+ People.Clear();
+ }
+
+ public UserItemData GetOrAddUserData(string userId)
+ {
+ if (UserDataList == null)
+ {
+ UserDataList = new List();
+ }
+
+ var userData = UserDataList.FirstOrDefault(i => string.Equals(userId, i.UserId.ToString("N"), StringComparison.OrdinalIgnoreCase));
+
+ if (userData == null)
+ {
+ userData = new UserItemData()
+ {
+ UserId = new Guid(userId)
+ };
+
+ UserDataList.Add(userData);
+ }
+
+ return userData;
}
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 64b20c13ef..b3e82f9258 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -125,6 +125,12 @@ namespace MediaBrowser.Controller.Session
/// The session controller.
public ISessionController SessionController { get; set; }
+ ///
+ /// Gets or sets the application icon URL.
+ ///
+ /// The application icon URL.
+ public string AppIconUrl { get; set; }
+
///
/// Gets or sets the supported commands.
///
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
index 860c736ead..36ad27290d 100644
--- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
@@ -63,4 +63,15 @@ namespace MediaBrowser.Controller.Sync
/// Task<SyncedFileInfo>.
Task SendFile(string path, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken);
}
+
+ public interface IHasDuplicateCheck
+ {
+ ///
+ /// Allows the duplicate job item.
+ ///
+ /// The original.
+ /// The duplicate.
+ /// true if XXXX, false otherwise.
+ bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate);
+ }
}
diff --git a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
index 7e896492da..f26ceff905 100644
--- a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
+++ b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Dlna.Channels
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
- private DeviceDiscovery _deviceDiscovery;
+ private readonly IDeviceDiscovery _deviceDiscovery;
private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1);
private List _servers = new List();
@@ -33,21 +33,21 @@ namespace MediaBrowser.Dlna.Channels
private Func> _localServersLookup;
- public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger)
+ public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IDeviceDiscovery deviceDiscovery)
{
_config = config;
_httpClient = httpClient;
_logger = logger;
+ _deviceDiscovery = deviceDiscovery;
Instance = this;
}
- internal void Start(DeviceDiscovery deviceDiscovery, Func> localServersLookup)
+ internal void Start(Func> localServersLookup)
{
_localServersLookup = localServersLookup;
- _deviceDiscovery = deviceDiscovery;
//deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered;
- deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft;
+ _deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft;
}
async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
index 50a6f3ba61..390f442670 100644
--- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs
+++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
@@ -120,7 +120,7 @@ namespace MediaBrowser.Dlna.Didl
}
}
- AddCover(item, null, element);
+ AddCover(item, context, null, element);
return element;
}
@@ -193,6 +193,9 @@ namespace MediaBrowser.Dlna.Didl
if (string.Equals(subtitleMode, "CaptionInfoEx", StringComparison.OrdinalIgnoreCase))
{
+ // http://192.168.1.3:9999/video.srt
+ // http://192.168.1.3:9999/video.srt
+
//var res = container.OwnerDocument.CreateElement("SEC", "CaptionInfoEx");
//res.InnerText = info.Url;
@@ -201,6 +204,16 @@ namespace MediaBrowser.Dlna.Didl
//res.SetAttribute("type", info.Format.ToLower());
//container.AppendChild(res);
}
+ else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
+ {
+ var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
+
+ res.InnerText = info.Url;
+
+ res.SetAttribute("protocolInfo", "http-get:*:smi/caption:*");
+
+ container.AppendChild(res);
+ }
else
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
@@ -481,7 +494,7 @@ namespace MediaBrowser.Dlna.Didl
AddCommonFields(folder, stubType, null, container, filter);
- AddCover(folder, stubType, container);
+ AddCover(folder, context, stubType, container);
return container;
}
@@ -764,7 +777,7 @@ namespace MediaBrowser.Dlna.Didl
}
}
- private void AddCover(BaseItem item, StubType? stubType, XmlElement element)
+ private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlElement element)
{
if (stubType.HasValue && stubType.Value == StubType.People)
{
@@ -772,7 +785,26 @@ namespace MediaBrowser.Dlna.Didl
return;
}
- var imageInfo = GetImageInfo(item);
+ ImageDownloadInfo imageInfo = null;
+
+ if (context is UserView)
+ {
+ var episode = item as Episode;
+ if (episode != null)
+ {
+ var parent = (BaseItem)episode.Series ?? episode.Season;
+ if (parent != null)
+ {
+ imageInfo = GetImageInfo(parent);
+ }
+ }
+ }
+
+ // Finally, just use the image from the item
+ if (imageInfo == null)
+ {
+ imageInfo = GetImageInfo(item);
+ }
if (imageInfo == null)
{
@@ -850,7 +882,7 @@ namespace MediaBrowser.Dlna.Didl
private void AddEmbeddedImageAsCover(string name, XmlElement element)
{
var result = element.OwnerDocument;
-
+
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
@@ -925,14 +957,11 @@ namespace MediaBrowser.Dlna.Didl
}
}
- if (item is Audio || item is Episode)
- {
- item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
+ item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
- if (item != null)
- {
- return GetImageInfo(item, ImageType.Primary);
- }
+ if (item != null)
+ {
+ return GetImageInfo(item, ImageType.Primary);
}
return null;
@@ -955,17 +984,17 @@ namespace MediaBrowser.Dlna.Didl
int? width = null;
int? height = null;
- try
- {
- var size = _imageProcessor.GetImageSize(imageInfo);
+ //try
+ //{
+ // var size = _imageProcessor.GetImageSize(imageInfo);
- width = Convert.ToInt32(size.Width);
- height = Convert.ToInt32(size.Height);
- }
- catch
- {
+ // width = Convert.ToInt32(size.Width);
+ // height = Convert.ToInt32(size.Height);
+ //}
+ //catch
+ //{
- }
+ //}
return new ImageDownloadInfo
{
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 9ce62034b4..95e2aaa77c 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -530,6 +530,7 @@ namespace MediaBrowser.Dlna
new SonyBravia2011Profile(),
new SonyBravia2012Profile(),
new SonyBravia2013Profile(),
+ new SonyBravia2014Profile(),
new SonyBlurayPlayer2013Profile(),
new SonyBlurayPlayerProfile(),
new PanasonicVieraProfile(),
@@ -547,7 +548,8 @@ namespace MediaBrowser.Dlna
new DefaultProfile(),
new PopcornHourProfile(),
new VlcProfile(),
- new BubbleUpnpProfile()
+ new BubbleUpnpProfile(),
+ new KodiProfile(),
};
foreach (var item in list)
diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
index f9f9c3f18b..bdb778cab7 100644
--- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
+++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
@@ -38,7 +38,7 @@ namespace MediaBrowser.Dlna.Main
private readonly IMediaSourceManager _mediaSourceManager;
private readonly SsdpHandler _ssdpHandler;
- private DeviceDiscovery _deviceDiscovery;
+ private readonly IDeviceDiscovery _deviceDiscovery;
private readonly List _registeredServerIds = new List();
private bool _dlnaServerStarted;
@@ -56,7 +56,7 @@ namespace MediaBrowser.Dlna.Main
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
- ISsdpHandler ssdpHandler)
+ ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery)
{
_config = config;
_appHost = appHost;
@@ -70,6 +70,7 @@ namespace MediaBrowser.Dlna.Main
_userDataManager = userDataManager;
_localization = localization;
_mediaSourceManager = mediaSourceManager;
+ _deviceDiscovery = deviceDiscovery;
_ssdpHandler = (SsdpHandler)ssdpHandler;
_logger = logManager.GetLogger("Dlna");
}
@@ -81,7 +82,7 @@ namespace MediaBrowser.Dlna.Main
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
- DlnaChannelFactory.Instance.Start(_deviceDiscovery, () => _registeredServerIds);
+ DlnaChannelFactory.Instance.Start(() => _registeredServerIds);
}
void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
@@ -125,9 +126,7 @@ namespace MediaBrowser.Dlna.Main
{
_ssdpHandler.Start();
- _deviceDiscovery = new DeviceDiscovery(_logger, _config, _ssdpHandler, _appHost);
-
- _deviceDiscovery.Start();
+ ((DeviceDiscovery)_deviceDiscovery).Start(_ssdpHandler);
}
catch (Exception ex)
{
@@ -135,18 +134,6 @@ namespace MediaBrowser.Dlna.Main
}
}
- private void DisposeDeviceDiscovery()
- {
- try
- {
- _deviceDiscovery.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing device discovery", ex);
- }
- }
-
public void StartDlnaServer()
{
try
@@ -240,7 +227,6 @@ namespace MediaBrowser.Dlna.Main
{
DisposeDlnaServer();
DisposePlayToManager();
- DisposeDeviceDiscovery();
}
public void DisposeDlnaServer()
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index 06aaff734e..c49cbc6548 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -81,7 +81,9 @@
+
+
@@ -166,7 +168,9 @@
-
+
+ Designer
+
@@ -175,7 +179,9 @@
-
+
+ Designer
+
@@ -214,6 +220,16 @@
+
+
+ Designer
+
+
+
+
+ Designer
+
+
");
}
- var version = GetType().Assembly.GetName().Version;
+ var versionString = !string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase) ? "?v=" + appVersion : string.Empty;
- var imports = new string[]
+ var imports = new[]
{
- "vulcanize-out.html"
+ "vulcanize-out.html" + versionString
};
var importsHtml = string.Join("", imports.Select(i => "").ToArray());
@@ -315,7 +319,9 @@ namespace MediaBrowser.WebDashboard.Api
// In chrome it is causing the body to be hidden while loading, which leads to width-check methods to return 0 for everything
//imports = "";
- html = html.Replace("