فهرست منبع

Merge branch 'dev' of https://github.com/MediaBrowser/MediaBrowser into dev

Luke Pulverenti 10 سال پیش
والد
کامیت
78a25d2289

+ 15 - 5
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -84,6 +84,8 @@
     <Compile Include="Playback\MediaInfoService.cs" />
     <Compile Include="Playback\TranscodingThrottler.cs" />
     <Compile Include="PlaylistService.cs" />
+    <Compile Include="Reports\Activities\ReportActivitiesBuilder.cs" />
+    <Compile Include="Reports\Common\HeaderActivitiesMetadata.cs" />
     <Compile Include="Reports\Common\HeaderMetadata.cs" />
     <Compile Include="Reports\Common\ItemViewType.cs" />
     <Compile Include="Reports\Common\ReportBuilderBase.cs" />
@@ -91,15 +93,16 @@
     <Compile Include="Reports\Common\ReportFieldType.cs" />
     <Compile Include="Reports\Common\ReportHeaderIdType.cs" />
     <Compile Include="Reports\Common\ReportHelper.cs" />
+    <Compile Include="Reports\Common\ReportIncludeItemTypes.cs" />
     <Compile Include="Reports\Common\ReportViewType.cs" />
     <Compile Include="Reports\Data\ReportBuilder.cs" />
     <Compile Include="Reports\Data\ReportExport.cs" />
-    <Compile Include="Reports\Data\ReportGroup.cs" />
-    <Compile Include="Reports\Data\ReportHeader.cs" />
-    <Compile Include="Reports\Data\ReportItem.cs" />
     <Compile Include="Reports\Data\ReportOptions.cs" />
-    <Compile Include="Reports\Data\ReportResult.cs" />
-    <Compile Include="Reports\Data\ReportRow.cs" />
+    <Compile Include="Reports\Model\ReportGroup.cs" />
+    <Compile Include="Reports\Model\ReportHeader.cs" />
+    <Compile Include="Reports\Model\ReportItem.cs" />
+    <Compile Include="Reports\Model\ReportResult.cs" />
+    <Compile Include="Reports\Model\ReportRow.cs" />
     <Compile Include="Reports\ReportRequests.cs" />
     <Compile Include="Reports\ReportsService.cs" />
     <Compile Include="Reports\Stat\ReportStatBuilder.cs" />
@@ -205,6 +208,13 @@
     <PostBuildEvent>
     </PostBuildEvent>
   </PropertyGroup>
+  <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
+  </Target>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
   <Target Name="BeforeBuild">

+ 250 - 0
MediaBrowser.Api/Reports/Activities/ReportActivitiesBuilder.cs

@@ -0,0 +1,250 @@
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Querying;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Library;
+namespace MediaBrowser.Api.Reports
+{
+    /// <summary> A report activities builder. </summary>
+    /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
+    public class ReportActivitiesBuilder : ReportBuilderBase
+    {
+        #region [Constructors]
+
+        /// <summary>
+        /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportActivitiesBuilder class. </summary>
+        /// <param name="libraryManager"> Manager for library. </param>
+        /// <param name="userManager"> Manager for user. </param>
+        public ReportActivitiesBuilder(ILibraryManager libraryManager, IUserManager userManager)
+            : base(libraryManager)
+        {
+            _userManager = userManager;
+        }
+
+        #endregion
+
+        #region [Private Fields]
+
+        private readonly IUserManager _userManager; ///< Manager for user
+
+        #endregion
+
+        #region [Public Methods]
+
+        /// <summary> Gets a result. </summary>
+        /// <param name="queryResult"> The query result. </param>
+        /// <param name="request"> The request. </param>
+        /// <returns> The result. </returns>
+        public ReportResult GetResult(QueryResult<ActivityLogEntry> queryResult, IReportsQuery request)
+        {
+            List<ReportOptions<ActivityLogEntry>> options = this.GetReportOptions<ActivityLogEntry>(request,
+                () => this.GetDefaultHeaderMetadata(),
+                (hm) => this.GetOption(hm)).Where(x => x.Header.Visible == true).ToList();
+
+            var headers = GetHeaders<ActivityLogEntry>(options);
+            var rows = GetReportRows(queryResult.Items, options);
+
+            ReportResult result = new ReportResult { Headers = headers };
+            HeaderMetadata groupBy = ReportHelper.GetHeaderMetadataType(request.GroupBy);
+            int i = headers.FindIndex(x => x.FieldName == groupBy);
+            if (groupBy != HeaderMetadata.None && i >= 0)
+            {
+                var rowsGroup = rows.SelectMany(x => x.Columns[i].Name.Split(';'), (x, g) => new { Group = g.Trim(), Rows = x })
+                    .GroupBy(x => x.Group)
+                    .OrderBy(x => x.Key)
+                    .Select(x => new ReportGroup { Name = x.Key, Rows = x.Select(r => r.Rows).ToList() });
+
+                result.Groups = rowsGroup.ToList();
+                result.IsGrouped = true;
+            }
+            else
+            {
+                result.Rows = rows;
+                result.IsGrouped = false;
+            }
+
+            return result;
+        }
+
+        #endregion
+
+        #region [Protected Internal Methods]
+
+        /// <summary> Gets the headers. </summary>
+        /// <typeparam name="H"> Type of the header. </typeparam>
+        /// <param name="request"> The request. </param>
+        /// <returns> The headers. </returns>
+        /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/>
+        protected internal override List<ReportHeader> GetHeaders<H>(H request)
+        {
+            return this.GetHeaders<ActivityLogEntry>(request, () => this.GetDefaultHeaderMetadata(), (hm) => this.GetOption(hm));
+        }
+
+        #endregion
+
+        #region [Private Methods]
+
+        /// <summary> Gets default header metadata. </summary>
+        /// <returns> The default header metadata. </returns>
+        private List<HeaderMetadata> GetDefaultHeaderMetadata()
+        {
+            return new List<HeaderMetadata>
+					{
+                        HeaderMetadata.Date,
+                        HeaderMetadata.User,
+                        HeaderMetadata.Type,
+                        HeaderMetadata.Severity,
+						HeaderMetadata.Name,
+                        HeaderMetadata.ShortOverview,
+						HeaderMetadata.Overview,
+						//HeaderMetadata.UserPrimaryImageTag,
+                        //HeaderMetadata.Item,
+					};
+        }
+
+        /// <summary> Gets an option. </summary>
+        /// <param name="header"> The header. </param>
+        /// <param name="sortField"> The sort field. </param>
+        /// <returns> The option. </returns>
+        private ReportOptions<ActivityLogEntry> GetOption(HeaderMetadata header, string sortField = "")
+        {
+            HeaderMetadata internalHeader = header;
+
+            ReportOptions<ActivityLogEntry> option = new ReportOptions<ActivityLogEntry>()
+            {
+                Header = new ReportHeader
+                {
+                    HeaderFieldType = ReportFieldType.String,
+                    SortField = sortField,
+                    Type = "",
+                    ItemViewType = ItemViewType.None
+                }
+            };
+
+            switch (header)
+            {
+                case HeaderMetadata.StatusImage:
+                    option.Header.ItemViewType = ItemViewType.StatusImage;
+                    internalHeader = HeaderMetadata.Status;
+                    option.Header.CanGroup = false;
+                    break;
+                case HeaderMetadata.Name:
+                    option.Column = (i, r) => i.Name;
+                    break;
+                case HeaderMetadata.Overview:
+                    option.Column = (i, r) => i.Overview;
+                    option.Header.SortField = "";
+                    option.Header.CanGroup = false;
+                    break;
+
+                case HeaderMetadata.ShortOverview:
+                    option.Column = (i, r) => i.ShortOverview;
+                    option.Header.SortField = "";
+                    option.Header.CanGroup = false;
+                    break;
+
+                case HeaderMetadata.Type:
+                    option.Column = (i, r) => i.Type;
+                    option.Header.SortField = "";
+                    break;
+
+                case HeaderMetadata.Date:
+                    option.Column = (i, r) => i.Date;
+                    option.Header.SortField = "";
+                    option.Header.HeaderFieldType = ReportFieldType.DateTime;
+                    option.Header.Type = "";
+                    break;
+
+                case HeaderMetadata.UserPrimaryImageTag:
+                    //option.Column = (i, r) => i.UserPrimaryImageTag;
+                    option.Column = (i, r) =>
+                    {
+                        if (!string.IsNullOrEmpty(i.UserId))
+                        {
+                            MediaBrowser.Controller.Entities.User user = _userManager.GetUserById(i.UserId);
+                            if (user != null)
+                                return user.PrimaryImagePath;
+                        }
+                        return string.Empty;
+                    };
+                    option.Header.SortField = "";
+                    break;
+                case HeaderMetadata.Severity:
+                    option.Column = (i, r) => i.Severity;
+                    option.Header.SortField = "";
+                    break;
+                case HeaderMetadata.Item:
+                    option.Column = (i, r) => i.ItemId;
+                    option.Header.SortField = "";
+                    break;
+                case HeaderMetadata.User:
+                    option.Column = (i, r) =>
+                    {
+                        if (!string.IsNullOrEmpty(i.UserId))
+                        {
+                            MediaBrowser.Controller.Entities.User user = _userManager.GetUserById(i.UserId);
+                            if (user != null)
+                                return user.Name;
+                        }
+                        return string.Empty;
+                    };
+                    option.Header.SortField = "";
+                    break;
+
+            }
+
+            option.Header.Name = GetLocalizedHeader(internalHeader);
+            option.Header.FieldName = header;
+
+            return option;
+        }
+
+        /// <summary> Gets report rows. </summary>
+        /// <param name="items"> The items. </param>
+        /// <param name="options"> Options for controlling the operation. </param>
+        /// <returns> The report rows. </returns>
+        private List<ReportRow> GetReportRows(IEnumerable<ActivityLogEntry> items, List<ReportOptions<ActivityLogEntry>> options)
+        {
+            var rows = new List<ReportRow>();
+
+            foreach (ActivityLogEntry item in items)
+            {
+                ReportRow rRow = GetRow(item);
+                foreach (ReportOptions<ActivityLogEntry> option in options)
+                {
+                    object itemColumn = option.Column != null ? option.Column(item, rRow) : "";
+                    object itemId = option.ItemID != null ? option.ItemID(item) : "";
+                    ReportItem rItem = new ReportItem
+                    {
+                        Name = ReportHelper.ConvertToString(itemColumn, option.Header.HeaderFieldType),
+                        Id = ReportHelper.ConvertToString(itemId, ReportFieldType.Object)
+                    };
+                    rRow.Columns.Add(rItem);
+                }
+
+                rows.Add(rRow);
+            }
+
+            return rows;
+        }
+
+        /// <summary> Gets a row. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The row. </returns>
+        private ReportRow GetRow(ActivityLogEntry item)
+        {
+            ReportRow rRow = new ReportRow
+            {
+                Id = item.Id,
+
+            };
+            return rRow;
+        }
+
+        #endregion
+
+    }
+}

+ 20 - 0
MediaBrowser.Api/Reports/Common/HeaderActivitiesMetadata.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Api.Reports
+{
+    public enum HeaderActivitiesMetadata
+	{
+        None,
+        Name,
+        Overview,
+        ShortOverview,
+        Type,
+        Date,
+        UserPrimaryImageTag,
+        Severity,
+        Item,
+        User
+	}
+}

+ 10 - 1
MediaBrowser.Api/Reports/Common/HeaderMetadata.cs

@@ -42,6 +42,15 @@ namespace MediaBrowser.Api.Reports
 		AudioAlbumArtist,
 		MusicArtist,
 		AudioAlbum,
-		Status
+		Status,
+        //Activity logs
+        Overview,
+        ShortOverview,
+        Type,
+        Date,
+        UserPrimaryImageTag,
+        Severity,
+        Item,
+        User
 	}
 }

+ 325 - 212
MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs

@@ -13,217 +13,330 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Reports
 {
-	/// <summary> A report builder base. </summary>
-	public class ReportBuilderBase
-	{
-		/// <summary>
-		/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilderBase class. </summary>
-		/// <param name="libraryManager"> Manager for library. </param>
-		public ReportBuilderBase(ILibraryManager libraryManager)
-		{
-			_libraryManager = libraryManager;
-		}
-
-		/// <summary> Manager for library. </summary>
-		protected readonly ILibraryManager _libraryManager;
-
-		/// <summary> Gets audio stream. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The audio stream. </returns>
-		protected string GetAudioStream(BaseItem item)
-		{
-			var stream = GetStream(item, MediaStreamType.Audio);
-			if (stream != null)
-				return stream.Codec.ToUpper() == "DCA" ? stream.Profile : stream.Codec.
-				ToUpper();
-
-			return string.Empty;
-		}
-
-		/// <summary> Gets an episode. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The episode. </returns>
-		protected string GetEpisode(BaseItem item)
-		{
-
-			if (item.GetClientTypeName() == ChannelMediaContentType.Episode.ToString() && item.ParentIndexNumber != null)
-				return "Season " + item.ParentIndexNumber;
-			else
-				return item.Name;
-		}
-
-		/// <summary> Gets a genre. </summary>
-		/// <param name="name"> The name. </param>
-		/// <returns> The genre. </returns>
-		protected Genre GetGenre(string name)
-		{
-			if (string.IsNullOrEmpty(name))
-				return null;
-			return _libraryManager.GetGenre(name);
-		}
-
-		/// <summary> Gets genre identifier. </summary>
-		/// <param name="name"> The name. </param>
-		/// <returns> The genre identifier. </returns>
-		protected string GetGenreID(string name)
-		{
-			if (string.IsNullOrEmpty(name))
-				return string.Empty;
-			return string.Format("{0:N}",
-					GetGenre(name).Id);
-		}
-
-		/// <summary> Gets list as string. </summary>
-		/// <param name="items"> The items. </param>
-		/// <returns> The list as string. </returns>
-		protected string GetListAsString(List<string> items)
-		{
-			return String.Join("; ", items);
-		}
-
-		/// <summary> Gets media source information. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The media source information. </returns>
-		protected MediaSourceInfo GetMediaSourceInfo(BaseItem item)
-		{
-			var mediaSource = item as IHasMediaSources;
-			if (mediaSource != null)
-				return mediaSource.GetMediaSources(false).FirstOrDefault(n => n.Type == MediaSourceType.Default);
-
-			return null;
-		}
-
-		/// <summary> Gets an object. </summary>
-		/// <typeparam name="T"> Generic type parameter. </typeparam>
-		/// <typeparam name="R"> Type of the r. </typeparam>
-		/// <param name="item"> The item. </param>
-		/// <param name="function"> The function. </param>
-		/// <param name="defaultValue"> The default value. </param>
-		/// <returns> The object. </returns>
-		protected R GetObject<T, R>(BaseItem item, Func<T, R> function, R defaultValue = default(R)) where T : class
-		{
-			var value = item as T;
-			if (value != null && function != null)
-				return function(value);
-			else
-				return defaultValue;
-		}
-
-		/// <summary> Gets a person. </summary>
-		/// <param name="name"> The name. </param>
-		/// <returns> The person. </returns>
-		protected Person GetPerson(string name)
-		{
-			if (string.IsNullOrEmpty(name))
-				return null;
-			return _libraryManager.GetPerson(name);
-		}
-
-		/// <summary> Gets person identifier. </summary>
-		/// <param name="name"> The name. </param>
-		/// <returns> The person identifier. </returns>
-		protected string GetPersonID(string name)
-		{
-			if (string.IsNullOrEmpty(name))
-				return string.Empty;
-			return string.Format("{0:N}",
-					GetPerson(name).Id);
-		}
-
-		/// <summary> Gets runtime date time. </summary>
-		/// <param name="runtime"> The runtime. </param>
-		/// <returns> The runtime date time. </returns>
-		protected double? GetRuntimeDateTime(long? runtime)
-		{
-			if (runtime.HasValue)
+    /// <summary> A report builder base. </summary>
+    public abstract class ReportBuilderBase
+    {
+
+        #region [Constructors]
+
+        /// <summary>
+        /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilderBase class. </summary>
+        /// <param name="libraryManager"> Manager for library. </param>
+        public ReportBuilderBase(ILibraryManager libraryManager)
+        {
+            _libraryManager = libraryManager;
+        }
+
+        #endregion
+
+        #region [Protected Fields]
+
+        /// <summary> Manager for library. </summary>
+        protected readonly ILibraryManager _libraryManager; ///< Manager for library
+
+        protected Func<bool, string> GetBoolString = s => s == true ? "x" : ""; ///< .
+
+        #endregion
+
+        #region [Protected Internal Methods]
+
+        /// <summary> Gets the headers. </summary>
+        /// <typeparam name="H"> Type of the header. </typeparam>
+        /// <param name="request"> The request. </param>
+        /// <returns> The headers. </returns>
+        protected internal abstract List<ReportHeader> GetHeaders<H>(H request) where H : IReportsHeader;
+
+        #endregion
+
+        #region [Protected Methods]
+
+        /// <summary> Gets active headers. </summary>
+        /// <typeparam name="T"> Generic type parameter. </typeparam>
+        /// <param name="options"> Options for controlling the operation. </param>
+        /// <returns> The active headers. </returns>
+        protected List<ReportHeader> GetActiveHeaders<T>(List<ReportOptions<T>> options)
+        {
+            List<ReportHeader> headers = new List<ReportHeader>();
+            foreach (ReportOptions<T> option in options.Where(x => x.Header.Visible == true))
+            {
+                headers.Add(option.Header);
+            }
+
+            return headers;
+        }
+
+        /// <summary> Gets audio stream. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The audio stream. </returns>
+        protected string GetAudioStream(BaseItem item)
+        {
+            var stream = GetStream(item, MediaStreamType.Audio);
+            if (stream != null)
+                return stream.Codec.ToUpper() == "DCA" ? stream.Profile : stream.Codec.
+                ToUpper();
+
+            return string.Empty;
+        }
+
+        /// <summary> Gets an episode. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The episode. </returns>
+        protected string GetEpisode(BaseItem item)
+        {
+
+            if (item.GetClientTypeName() == ChannelMediaContentType.Episode.ToString() && item.ParentIndexNumber != null)
+                return "Season " + item.ParentIndexNumber;
+            else
+                return item.Name;
+        }
+
+        /// <summary> Gets a genre. </summary>
+        /// <param name="name"> The name. </param>
+        /// <returns> The genre. </returns>
+        protected Genre GetGenre(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                return null;
+            return _libraryManager.GetGenre(name);
+        }
+
+        /// <summary> Gets genre identifier. </summary>
+        /// <param name="name"> The name. </param>
+        /// <returns> The genre identifier. </returns>
+        protected string GetGenreID(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                return string.Empty;
+            return string.Format("{0:N}",
+                    GetGenre(name).Id);
+        }
+
+        /// <summary> Gets the headers. </summary>
+        /// <typeparam name="T"> Generic type parameter. </typeparam>
+        /// <param name="options"> Options for controlling the operation. </param>
+        /// <returns> The headers. </returns>
+        protected List<ReportHeader> GetHeaders<T>(List<ReportOptions<T>> options)
+        {
+            List<ReportHeader> headers = new List<ReportHeader>();
+            foreach (ReportOptions<T> option in options)
+            {
+                headers.Add(option.Header);
+            }
+
+            return headers;
+        }
+
+        /// <summary> Gets the headers. </summary>
+        /// <typeparam name="T"> Generic type parameter. </typeparam>
+        /// <param name="request"> The request. </param>
+        /// <param name="getHeadersMetadata"> The get headers metadata. </param>
+        /// <param name="getOptions"> Options for controlling the get. </param>
+        /// <returns> The headers. </returns>
+        protected List<ReportHeader> GetHeaders<T>(IReportsHeader request, Func<List<HeaderMetadata>> getHeadersMetadata, Func<HeaderMetadata, ReportOptions<T>> getOptions)
+        {
+            List<ReportOptions<T>> options = this.GetReportOptions(request, getHeadersMetadata, getOptions);
+            return this.GetHeaders(options);
+        }
+
+        /// <summary> Gets list as string. </summary>
+        /// <param name="items"> The items. </param>
+        /// <returns> The list as string. </returns>
+        protected string GetListAsString(List<string> items)
+        {
+            return String.Join("; ", items);
+        }
+
+        /// <summary> Gets localized header. </summary>
+        /// <param name="internalHeader"> The internal header. </param>
+        /// <returns> The localized header. </returns>
+        protected static string GetLocalizedHeader(HeaderMetadata internalHeader)
+        {
+            string headerName = "";
+            if (internalHeader != HeaderMetadata.None)
+            {
+                string localHeader = "Header" + internalHeader.ToString();
+                headerName = internalHeader != HeaderMetadata.None ? ReportHelper.GetJavaScriptLocalizedString(localHeader) : "";
+                if (string.Compare(localHeader, headerName, StringComparison.CurrentCultureIgnoreCase) == 0)
+                    headerName = ReportHelper.GetServerLocalizedString(localHeader);
+            }
+            return headerName;
+        }
+
+        /// <summary> Gets media source information. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The media source information. </returns>
+        protected MediaSourceInfo GetMediaSourceInfo(BaseItem item)
+        {
+            var mediaSource = item as IHasMediaSources;
+            if (mediaSource != null)
+                return mediaSource.GetMediaSources(false).FirstOrDefault(n => n.Type == MediaSourceType.Default);
+
+            return null;
+        }
+
+        /// <summary> Gets an object. </summary>
+        /// <typeparam name="T"> Generic type parameter. </typeparam>
+        /// <typeparam name="R"> Type of the r. </typeparam>
+        /// <param name="item"> The item. </param>
+        /// <param name="function"> The function. </param>
+        /// <param name="defaultValue"> The default value. </param>
+        /// <returns> The object. </returns>
+        protected R GetObject<T, R>(BaseItem item, Func<T, R> function, R defaultValue = default(R)) where T : class
+        {
+            var value = item as T;
+            if (value != null && function != null)
+                return function(value);
+            else
+                return defaultValue;
+        }
+
+        /// <summary> Gets a person. </summary>
+        /// <param name="name"> The name. </param>
+        /// <returns> The person. </returns>
+        protected Person GetPerson(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                return null;
+            return _libraryManager.GetPerson(name);
+        }
+
+        /// <summary> Gets person identifier. </summary>
+        /// <param name="name"> The name. </param>
+        /// <returns> The person identifier. </returns>
+        protected string GetPersonID(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                return string.Empty;
+            return string.Format("{0:N}",
+                    GetPerson(name).Id);
+        }
+
+        /// <summary> Gets report options. </summary>
+        /// <typeparam name="T"> Generic type parameter. </typeparam>
+        /// <param name="request"> The request. </param>
+        /// <param name="getHeadersMetadata"> The get headers metadata. </param>
+        /// <param name="getOptions"> Options for controlling the get. </param>
+        /// <returns> The report options. </returns>
+        protected List<ReportOptions<T>> GetReportOptions<T>(IReportsHeader request, Func<List<HeaderMetadata>> getHeadersMetadata, Func<HeaderMetadata, ReportOptions<T>> getOptions)
+        {
+            List<HeaderMetadata> headersMetadata = getHeadersMetadata();
+            List<ReportOptions<T>> options = new List<ReportOptions<T>>();
+            foreach (HeaderMetadata header in headersMetadata)
+            {
+                options.Add(getOptions(header));
+            }
+
+            if (request != null && !string.IsNullOrEmpty(request.ReportColumns))
+            {
+                List<HeaderMetadata> headersMetadataFiltered = ReportHelper.GetFilteredReportHeaderMetadata(request.ReportColumns, () => headersMetadata);
+                foreach (ReportHeader header in options.Select(x => x.Header))
+                {
+                    if (!headersMetadataFiltered.Contains(header.FieldName))
+                    {
+                        header.Visible = false;
+                    }
+                }
+            }
+
+            return options;
+        }
+
+        /// <summary> Gets runtime date time. </summary>
+        /// <param name="runtime"> The runtime. </param>
+        /// <returns> The runtime date time. </returns>
+        protected double? GetRuntimeDateTime(long? runtime)
+        {
+            if (runtime.HasValue)
                 return Math.Ceiling(new TimeSpan(runtime.Value).TotalMinutes);
-			return null;
-		}
-
-		/// <summary> Gets series production year. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The series production year. </returns>
-		protected string GetSeriesProductionYear(BaseItem item)
-		{
-
-			string productionYear = item.ProductionYear.ToString();
-			var series = item as Series;
-			if (series == null)
-			{
-				if (item.ProductionYear == null || item.ProductionYear == 0)
-					return string.Empty;
-				return productionYear;
-			}
-
-			if (series.Status == SeriesStatus.Continuing)
-				return productionYear += "-Present";
-
-			if (series.EndDate != null && series.EndDate.Value.Year != series.ProductionYear)
-				return productionYear += "-" + series.EndDate.Value.Year;
-
-			return productionYear;
-		}
-
-		/// <summary> Gets a stream. </summary>
-		/// <param name="item"> The item. </param>
-		/// <param name="streamType"> Type of the stream. </param>
-		/// <returns> The stream. </returns>
-		protected MediaStream GetStream(BaseItem item, MediaStreamType streamType)
-		{
-			var itemInfo = GetMediaSourceInfo(item);
-			if (itemInfo != null)
-				return itemInfo.MediaStreams.FirstOrDefault(n => n.Type == streamType);
-
-			return null;
-		}
-
-		/// <summary> Gets a studio. </summary>
-		/// <param name="name"> The name. </param>
-		/// <returns> The studio. </returns>
-		protected Studio GetStudio(string name)
-		{
-			if (string.IsNullOrEmpty(name))
-				return null;
-			return _libraryManager.GetStudio(name);
-		}
-
-		/// <summary> Gets studio identifier. </summary>
-		/// <param name="name"> The name. </param>
-		/// <returns> The studio identifier. </returns>
-		protected string GetStudioID(string name)
-		{
-			if (string.IsNullOrEmpty(name))
-				return string.Empty;
-			return string.Format("{0:N}",
-					GetStudio(name).Id);
-		}
-
-		/// <summary> Gets video resolution. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The video resolution. </returns>
-		protected string GetVideoResolution(BaseItem item)
-		{
-			var stream = GetStream(item,
-					MediaStreamType.Video);
-			if (stream != null && stream.Width != null)
-				return string.Format("{0} * {1}",
-						stream.Width,
-						(stream.Height != null ? stream.Height.ToString() : "-"));
-
-			return string.Empty;
-		}
-
-		/// <summary> Gets video stream. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The video stream. </returns>
-		protected string GetVideoStream(BaseItem item)
-		{
-			var stream = GetStream(item, MediaStreamType.Video);
-			if (stream != null)
-				return stream.Codec.ToUpper();
-
-			return string.Empty;
-		}
-
-	}
+            return null;
+        }
+
+        /// <summary> Gets series production year. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The series production year. </returns>
+        protected string GetSeriesProductionYear(BaseItem item)
+        {
+
+            string productionYear = item.ProductionYear.ToString();
+            var series = item as Series;
+            if (series == null)
+            {
+                if (item.ProductionYear == null || item.ProductionYear == 0)
+                    return string.Empty;
+                return productionYear;
+            }
+
+            if (series.Status == SeriesStatus.Continuing)
+                return productionYear += "-Present";
+
+            if (series.EndDate != null && series.EndDate.Value.Year != series.ProductionYear)
+                return productionYear += "-" + series.EndDate.Value.Year;
+
+            return productionYear;
+        }
+
+        /// <summary> Gets a stream. </summary>
+        /// <param name="item"> The item. </param>
+        /// <param name="streamType"> Type of the stream. </param>
+        /// <returns> The stream. </returns>
+        protected MediaStream GetStream(BaseItem item, MediaStreamType streamType)
+        {
+            var itemInfo = GetMediaSourceInfo(item);
+            if (itemInfo != null)
+                return itemInfo.MediaStreams.FirstOrDefault(n => n.Type == streamType);
+
+            return null;
+        }
+
+        /// <summary> Gets a studio. </summary>
+        /// <param name="name"> The name. </param>
+        /// <returns> The studio. </returns>
+        protected Studio GetStudio(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                return null;
+            return _libraryManager.GetStudio(name);
+        }
+
+        /// <summary> Gets studio identifier. </summary>
+        /// <param name="name"> The name. </param>
+        /// <returns> The studio identifier. </returns>
+        protected string GetStudioID(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                return string.Empty;
+            return string.Format("{0:N}",
+                    GetStudio(name).Id);
+        }
+
+        /// <summary> Gets video resolution. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The video resolution. </returns>
+        protected string GetVideoResolution(BaseItem item)
+        {
+            var stream = GetStream(item,
+                    MediaStreamType.Video);
+            if (stream != null && stream.Width != null)
+                return string.Format("{0} * {1}",
+                        stream.Width,
+                        (stream.Height != null ? stream.Height.ToString() : "-"));
+
+            return string.Empty;
+        }
+
+        /// <summary> Gets video stream. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The video stream. </returns>
+        protected string GetVideoStream(BaseItem item)
+        {
+            var stream = GetStream(item, MediaStreamType.Video);
+            if (stream != null)
+                return stream.Codec.ToUpper();
+
+            return string.Empty;
+        }
+
+        #endregion
+
+    }
 }

+ 129 - 89
MediaBrowser.Api/Reports/Common/ReportHelper.cs

@@ -7,95 +7,135 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Reports
 {
-	public class ReportHelper
-	{
-		/// <summary> Gets java script localized string. </summary>
-		/// <param name="phrase"> The phrase. </param>
-		/// <returns> The java script localized string. </returns>
-		public static string GetJavaScriptLocalizedString(string phrase)
-		{
-			var dictionary = BaseItem.LocalizationManager.GetJavaScriptLocalizationDictionary(BaseItem.ConfigurationManager.Configuration.UICulture);
-
-			string value;
-
-			if (dictionary.TryGetValue(phrase, out value))
-			{
-				return value;
-			}
-
-			return phrase;
-		}
-
-		/// <summary> Gets server localized string. </summary>
-		/// <param name="phrase"> The phrase. </param>
-		/// <returns> The server localized string. </returns>
-		public static string GetServerLocalizedString(string phrase)
-		{
-			return BaseItem.LocalizationManager.GetLocalizedString(phrase, BaseItem.ConfigurationManager.Configuration.UICulture);
-		}
-
-		/// <summary> Gets row type. </summary>
-		/// <param name="rowType"> The type. </param>
-		/// <returns> The row type. </returns>
-		public static ReportViewType GetRowType(string rowType)
-		{
-			if (string.IsNullOrEmpty(rowType))
-				return ReportViewType.BaseItem;
-
-			ReportViewType rType;
-
-			if (!Enum.TryParse<ReportViewType>(rowType, out rType))
-				return ReportViewType.BaseItem;
-
-			return rType;
-		}
-
-		/// <summary> Gets header metadata type. </summary>
-		/// <param name="header"> The header. </param>
-		/// <returns> The header metadata type. </returns>
-		public static HeaderMetadata GetHeaderMetadataType(string header)
-		{
-			if (string.IsNullOrEmpty(header))
-				return HeaderMetadata.None;
-
-			HeaderMetadata rType;
-
-			if (!Enum.TryParse<HeaderMetadata>(header, out rType))
-				return HeaderMetadata.None;
-
-			return rType;
-		}
-
-		/// <summary> Convert field to string. </summary>
-		/// <typeparam name="T"> Generic type parameter. </typeparam>
-		/// <param name="value"> The value. </param>
-		/// <param name="fieldType"> Type of the field. </param>
-		/// <returns> The field converted to string. </returns>
-		public static string ConvertToString<T>(T value, ReportFieldType fieldType)
-		{
-			if (value == null)
-				return "";
-			switch (fieldType)
-			{
-				case ReportFieldType.String:
-					return value.ToString();
-				case ReportFieldType.Boolean:
-					return value.ToString();
-				case ReportFieldType.Date:
-					return string.Format("{0:d}", value);
-				case ReportFieldType.Time:
-					return string.Format("{0:t}", value);
-				case ReportFieldType.DateTime:
-					return string.Format("{0:d}", value);
+    /// <summary> A report helper. </summary>
+    public class ReportHelper
+    {
+        #region [Public Methods]
+
+        /// <summary> Convert field to string. </summary>
+        /// <typeparam name="T"> Generic type parameter. </typeparam>
+        /// <param name="value"> The value. </param>
+        /// <param name="fieldType"> Type of the field. </param>
+        /// <returns> The field converted to string. </returns>
+        public static string ConvertToString<T>(T value, ReportFieldType fieldType)
+        {
+            if (value == null)
+                return "";
+            switch (fieldType)
+            {
+                case ReportFieldType.String:
+                    return value.ToString();
+                case ReportFieldType.Boolean:
+                    return value.ToString();
+                case ReportFieldType.Date:
+                    return string.Format("{0:d}", value);
+                case ReportFieldType.Time:
+                    return string.Format("{0:t}", value);
+                case ReportFieldType.DateTime:
+                    return string.Format("{0:d}", value);
                 case ReportFieldType.Minutes:
                     return string.Format("{0}mn", value);
-				case ReportFieldType.Int:
-					return string.Format("", value);
-				default:
-					if (value is Guid)
-						return string.Format("{0:N}", value);
-					return value.ToString();
-			}
-		}
-	}
+                case ReportFieldType.Int:
+                    return string.Format("", value);
+                default:
+                    if (value is Guid)
+                        return string.Format("{0:N}", value);
+                    return value.ToString();
+            }
+        }
+
+        /// <summary> Gets filtered report header metadata. </summary>
+        /// <param name="reportColumns"> The report columns. </param>
+        /// <param name="defaultReturnValue"> The default return value. </param>
+        /// <returns> The filtered report header metadata. </returns>
+        public static List<HeaderMetadata> GetFilteredReportHeaderMetadata(string reportColumns, Func<List<HeaderMetadata>> defaultReturnValue = null)
+        {
+            if (!string.IsNullOrEmpty(reportColumns))
+            {
+                var s = reportColumns.Split('|').Select(x => ReportHelper.GetHeaderMetadataType(x)).Where(x => x != HeaderMetadata.None);
+                return s.ToList();
+            }
+            else
+                if (defaultReturnValue != null)
+                    return defaultReturnValue();
+                else
+                    return new List<HeaderMetadata>();
+        }
+
+        /// <summary> Gets header metadata type. </summary>
+        /// <param name="header"> The header. </param>
+        /// <returns> The header metadata type. </returns>
+        public static HeaderMetadata GetHeaderMetadataType(string header)
+        {
+            if (string.IsNullOrEmpty(header))
+                return HeaderMetadata.None;
+
+            HeaderMetadata rType;
+
+            if (!Enum.TryParse<HeaderMetadata>(header, out rType))
+                return HeaderMetadata.None;
+
+            return rType;
+        }
+
+        /// <summary> Gets java script localized string. </summary>
+        /// <param name="phrase"> The phrase. </param>
+        /// <returns> The java script localized string. </returns>
+        public static string GetJavaScriptLocalizedString(string phrase)
+        {
+            var dictionary = BaseItem.LocalizationManager.GetJavaScriptLocalizationDictionary(BaseItem.ConfigurationManager.Configuration.UICulture);
+
+            string value;
+
+            if (dictionary.TryGetValue(phrase, out value))
+            {
+                return value;
+            }
+
+            return phrase;
+        }
+
+        /// <summary> Gets report view type. </summary>
+        /// <param name="rowType"> The type. </param>
+        /// <returns> The report view type. </returns>
+        public static ReportViewType GetReportViewType(string rowType)
+        {
+            if (string.IsNullOrEmpty(rowType))
+                return ReportViewType.ReportData;
+
+            ReportViewType rType;
+
+            if (!Enum.TryParse<ReportViewType>(rowType, out rType))
+                return ReportViewType.ReportData;
+
+            return rType;
+        }
+
+        /// <summary> Gets row type. </summary>
+        /// <param name="rowType"> The type. </param>
+        /// <returns> The row type. </returns>
+        public static ReportIncludeItemTypes GetRowType(string rowType)
+        {
+            if (string.IsNullOrEmpty(rowType))
+                return ReportIncludeItemTypes.BaseItem;
+
+            ReportIncludeItemTypes rType;
+
+            if (!Enum.TryParse<ReportIncludeItemTypes>(rowType, out rType))
+                return ReportIncludeItemTypes.BaseItem;
+
+            return rType;
+        }
+
+        /// <summary> Gets server localized string. </summary>
+        /// <param name="phrase"> The phrase. </param>
+        /// <returns> The server localized string. </returns>
+        public static string GetServerLocalizedString(string phrase)
+        {
+            return BaseItem.LocalizationManager.GetLocalizedString(phrase, BaseItem.ConfigurationManager.Configuration.UICulture);
+        }
+
+        #endregion
+
+    }
 }

+ 25 - 0
MediaBrowser.Api/Reports/Common/ReportIncludeItemTypes.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Api.Reports
+{
+	public enum ReportIncludeItemTypes
+	{
+		MusicArtist,
+		MusicAlbum,
+		Book,
+		BoxSet,
+		Episode,
+		Game,
+		Video,
+		Movie,
+		MusicVideo,
+		Trailer,
+		Season,
+		Series,
+		Audio,
+		BaseItem,
+		Artist
+	}
+}

+ 4 - 15
MediaBrowser.Api/Reports/Common/ReportViewType.cs

@@ -6,20 +6,9 @@ namespace MediaBrowser.Api.Reports
 {
 	public enum ReportViewType
 	{
-		MusicArtist,
-		MusicAlbum,
-		Book,
-		BoxSet,
-		Episode,
-		Game,
-		Video,
-		Movie,
-		MusicVideo,
-		Trailer,
-		Season,
-		Series,
-		Audio,
-		BaseItem,
-		Artist
+        ReportData,
+        ReportStatistics,
+        ReportActivities
+
 	}
 }

+ 385 - 415
MediaBrowser.Api/Reports/Data/ReportBuilder.cs

@@ -17,162 +17,89 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Reports
 {
-	/// <summary> A report builder. </summary>
-	/// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
-	public class ReportBuilder : ReportBuilderBase
-	{
-
-		/// <summary>
-		/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilder class. </summary>
-		/// <param name="libraryManager"> Manager for library. </param>
-		public ReportBuilder(ILibraryManager libraryManager)
-			: base(libraryManager)
-		{
-		}
-
-		private Func<bool, string> GetBoolString = s => s == true ? "x" : "";
-
-		public ReportResult GetReportResult(BaseItem[] items, ReportViewType reportRowType, BaseReportRequest request)
-		{
-			List<HeaderMetadata> headersMetadata = this.GetFilteredReportHeaderMetadata(reportRowType, request);
-
-			var headers = GetReportHeaders(reportRowType, headersMetadata);
-			var rows = GetReportRows(items, headersMetadata);
-
-			ReportResult result = new ReportResult { Headers = headers };
-			HeaderMetadata groupBy = ReportHelper.GetHeaderMetadataType(request.GroupBy);
-			int i = headers.FindIndex(x => x.FieldName == groupBy);
-			if (groupBy != HeaderMetadata.None && i > 0)
-			{
-				var rowsGroup = rows.SelectMany(x => x.Columns[i].Name.Split(';'), (x, g) => new { Genre = g.Trim(), Rows = x })
-					.GroupBy(x => x.Genre)
-					.OrderBy(x => x.Key)
-					.Select(x => new ReportGroup { Name = x.Key, Rows = x.Select(r => r.Rows).ToList() });
-
-				result.Groups = rowsGroup.ToList();
-				result.IsGrouped = true;
-			}
-			else
-			{
-				result.Rows = rows;
-				result.IsGrouped = false;
-			}
-
-			return result;
-		}
-
-		public List<ReportHeader> GetReportHeaders(ReportViewType reportRowType, BaseReportRequest request)
-		{
-			List<ReportHeader> headersMetadata = this.GetReportHeaders(reportRowType);
-			if (request != null && !string.IsNullOrEmpty(request.ReportColumns))
-			{
-				List<HeaderMetadata> headersMetadataFiltered = this.GetFilteredReportHeaderMetadata(reportRowType, request);
-				foreach (ReportHeader reportHeader in headersMetadata)
-				{
-					if (!headersMetadataFiltered.Contains(reportHeader.FieldName))
-					{
-						reportHeader.Visible = false;
-					}
-				}
-
-
-			}
-
-			return headersMetadata;
-		}
-
-		public List<ReportHeader> GetReportHeaders(ReportViewType reportRowType, List<HeaderMetadata> headersMetadata = null)
-		{
-			if (headersMetadata == null)
-				headersMetadata = this.GetDefaultReportHeaderMetadata(reportRowType);
-
-			List<ReportOptions<BaseItem>> options = new List<ReportOptions<BaseItem>>();
-			foreach (HeaderMetadata header in headersMetadata)
-			{
-				options.Add(GetReportOption(header));
-			}
-
-
-			List<ReportHeader> headers = new List<ReportHeader>();
-			foreach (ReportOptions<BaseItem> option in options)
-			{
-				headers.Add(option.Header);
-			}
-			return headers;
-		}
-
-		private List<ReportRow> GetReportRows(IEnumerable<BaseItem> items, List<HeaderMetadata> headersMetadata)
-		{
-			List<ReportOptions<BaseItem>> options = new List<ReportOptions<BaseItem>>();
-			foreach (HeaderMetadata header in headersMetadata)
-			{
-				options.Add(GetReportOption(header));
-			}
-
-			var rows = new List<ReportRow>();
-
-			foreach (BaseItem item in items)
-			{
-				ReportRow rRow = GetRow(item);
-				foreach (ReportOptions<BaseItem> option in options)
-				{
-					object itemColumn = option.Column != null ? option.Column(item, rRow) : "";
-					object itemId = option.ItemID != null ? option.ItemID(item) : "";
-					ReportItem rItem = new ReportItem
-					{
-						Name = ReportHelper.ConvertToString(itemColumn, option.Header.HeaderFieldType),
-						Id = ReportHelper.ConvertToString(itemId, ReportFieldType.Object)
-					};
-					rRow.Columns.Add(rItem);
-				}
-
-				rows.Add(rRow);
-			}
-
-			return rows;
-		}
-
-		/// <summary> Gets a row. </summary>
-		/// <param name="item"> The item. </param>
-		/// <returns> The row. </returns>
-		private ReportRow GetRow(BaseItem item)
-		{
-			var hasTrailers = item as IHasTrailers;
-			var hasSpecialFeatures = item as IHasSpecialFeatures;
-			var video = item as Video;
-			ReportRow rRow = new ReportRow
-			{
-				Id = item.Id.ToString("N"),
-				HasLockData = item.IsLocked,
-				IsUnidentified = item.IsUnidentified,
-				HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false,
-				HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0),
-				HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0),
-				HasImageTagsLogo = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Logo) > 0),
-				HasSpecials = hasSpecialFeatures != null ? hasSpecialFeatures.SpecialFeatureIds.Count > 0 : false,
-				HasSubtitles = video != null ? video.HasSubtitles : false,
-				RowType = ReportHelper.GetRowType(item.GetClientTypeName())
-			};
-			return rRow;
-		}
-		public List<HeaderMetadata> GetFilteredReportHeaderMetadata(ReportViewType reportRowType, BaseReportRequest request)
-		{
-			if (request != null && !string.IsNullOrEmpty(request.ReportColumns))
-			{
-				var s = request.ReportColumns.Split('|').Select(x => ReportHelper.GetHeaderMetadataType(x)).Where(x => x != HeaderMetadata.None);
-				return s.ToList();
-			}
-			else
-				return this.GetDefaultReportHeaderMetadata(reportRowType);
-
-		}
-
-		public List<HeaderMetadata> GetDefaultReportHeaderMetadata(ReportViewType reportRowType)
-		{
-			switch (reportRowType)
-			{
-				case ReportViewType.Season:
-					return new List<HeaderMetadata>
+    /// <summary> A report builder. </summary>
+    /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
+    public class ReportBuilder : ReportBuilderBase
+    {
+
+        #region [Constructors]
+
+        /// <summary>
+        /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilder class. </summary>
+        /// <param name="libraryManager"> Manager for library. </param>
+        public ReportBuilder(ILibraryManager libraryManager)
+            : base(libraryManager)
+        {
+        }
+
+        #endregion
+
+        #region [Public Methods]
+
+        /// <summary> Gets report result. </summary>
+        /// <param name="items"> The items. </param>
+        /// <param name="request"> The request. </param>
+        /// <returns> The report result. </returns>
+        public ReportResult GetResult(BaseItem[] items, IReportsQuery request)
+        {
+            ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
+            List<ReportOptions<BaseItem>> options = this.GetReportOptions<BaseItem>(request,
+                () => this.GetDefaultHeaderMetadata(reportRowType),
+                (hm) => this.GetOption(hm)).Where(x => x.Header.Visible == true).ToList();
+
+            var headers = GetHeaders<BaseItem>(options);
+            var rows = GetReportRows(items, options);
+
+            ReportResult result = new ReportResult { Headers = headers };
+            HeaderMetadata groupBy = ReportHelper.GetHeaderMetadataType(request.GroupBy);
+            int i = headers.FindIndex(x => x.FieldName == groupBy);
+            if (groupBy != HeaderMetadata.None && i >= 0)
+            {
+                var rowsGroup = rows.SelectMany(x => x.Columns[i].Name.Split(';'), (x, g) => new { Group = g.Trim(), Rows = x })
+                    .GroupBy(x => x.Group)
+                    .OrderBy(x => x.Key)
+                    .Select(x => new ReportGroup { Name = x.Key, Rows = x.Select(r => r.Rows).ToList() });
+
+                result.Groups = rowsGroup.ToList();
+                result.IsGrouped = true;
+            }
+            else
+            {
+                result.Rows = rows;
+                result.IsGrouped = false;
+            }
+
+            return result;
+        }
+
+        #endregion
+
+        #region [Protected Internal Methods]
+
+        /// <summary> Gets the headers. </summary>
+        /// <typeparam name="H"> Type of the header. </typeparam>
+        /// <param name="request"> The request. </param>
+        /// <returns> The headers. </returns>
+        /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/>
+        protected internal override List<ReportHeader> GetHeaders<H>(H request)
+        {
+            ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
+            return this.GetHeaders<BaseItem>(request, () => this.GetDefaultHeaderMetadata(reportRowType), (hm) => this.GetOption(hm));
+        }
+
+        #endregion
+
+        #region [Private Methods]
+
+        /// <summary> Gets default report header metadata. </summary>
+        /// <param name="reportIncludeItemTypes"> Type of the report row. </param>
+        /// <returns> The default report header metadata. </returns>
+        private List<HeaderMetadata> GetDefaultHeaderMetadata(ReportIncludeItemTypes reportIncludeItemTypes)
+        {
+            switch (reportIncludeItemTypes)
+            {
+                case ReportIncludeItemTypes.Season:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Series,
@@ -183,8 +110,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Genres
 					};
 
-				case ReportViewType.Series:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Series:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -199,8 +126,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Specials
 					};
 
-				case ReportViewType.MusicAlbum:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.MusicAlbum:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -212,8 +139,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Genres
 					};
 
-				case ReportViewType.MusicArtist:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.MusicArtist:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.MusicArtist,
@@ -223,8 +150,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Genres
 					};
 
-				case ReportViewType.Game:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Game:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -239,8 +166,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Trailers
 					};
 
-				case ReportViewType.Movie:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Movie:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -259,8 +186,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Specials
 					};
 
-				case ReportViewType.Book:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Book:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -272,8 +199,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.CommunityRating
 					};
 
-				case ReportViewType.BoxSet:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.BoxSet:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -286,8 +213,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Trailers
 					};
 
-				case ReportViewType.Audio:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Audio:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -305,8 +232,8 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Audio
 					};
 
-				case ReportViewType.Episode:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Episode:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -327,12 +254,12 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Specials
 					};
 
-				case ReportViewType.Video:
-				case ReportViewType.MusicVideo:
-				case ReportViewType.Trailer:
-				case ReportViewType.BaseItem:
-				default:
-					return new List<HeaderMetadata>
+                case ReportIncludeItemTypes.Video:
+                case ReportIncludeItemTypes.MusicVideo:
+                case ReportIncludeItemTypes.Trailer:
+                case ReportIncludeItemTypes.BaseItem:
+                default:
+                    return new List<HeaderMetadata>
 					{
 						HeaderMetadata.StatusImage,
 						HeaderMetadata.Name,
@@ -351,239 +278,282 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Specials
 					};
 
-			}
-
-		}
-
-		/// <summary> Gets report option. </summary>
-		/// <param name="header"> The header. </param>
-		/// <param name="sortField"> The sort field. </param>
-		/// <returns> The report option. </returns>
-		private ReportOptions<BaseItem> GetReportOption(HeaderMetadata header, string sortField = "")
-		{
-			ReportHeader reportHeader = new ReportHeader
-			{
-				HeaderFieldType = ReportFieldType.String,
-				SortField = sortField,
-				Type = "",
-				ItemViewType = ItemViewType.None
-			};
-
-			Func<BaseItem, ReportRow, object> column = null;
-			Func<BaseItem, object> itemId = null;
-			HeaderMetadata internalHeader = header;
-
-			switch (header)
-			{
-				case HeaderMetadata.StatusImage:
-					reportHeader.ItemViewType = ItemViewType.StatusImage;
-					internalHeader = HeaderMetadata.Status;
-					reportHeader.CanGroup = false;
-					break;
-
-				case HeaderMetadata.Name:
-					column = (i, r) => i.Name;
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					reportHeader.SortField = "SortName";
-					break;
-
-				case HeaderMetadata.DateAdded:
-					column = (i, r) => i.DateCreated;
-					reportHeader.SortField = "DateCreated,SortName";
-					reportHeader.HeaderFieldType = ReportFieldType.DateTime;
-					reportHeader.Type = "";
-					break;
-
-				case HeaderMetadata.PremiereDate:
-				case HeaderMetadata.ReleaseDate:
-					column = (i, r) => i.PremiereDate;
-					reportHeader.HeaderFieldType = ReportFieldType.DateTime;
-					reportHeader.SortField = "ProductionYear,PremiereDate,SortName";
-					break;
-
-				case HeaderMetadata.Runtime:
-					column = (i, r) => this.GetRuntimeDateTime(i.RunTimeTicks);
-					reportHeader.HeaderFieldType = ReportFieldType.Minutes;
-					reportHeader.SortField = "Runtime,SortName";
-					break;
-
-				case HeaderMetadata.PlayCount:
-					reportHeader.HeaderFieldType = ReportFieldType.Int;
-					break;
-
-				case HeaderMetadata.Season:
-					column = (i, r) => this.GetEpisode(i);
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					reportHeader.SortField = "SortName";
-					break;
-
-				case HeaderMetadata.SeasonNumber:
-					column = (i, r) => this.GetObject<Season, string>(i, (x) => x.IndexNumber == null ? "" : x.IndexNumber.ToString());
-					reportHeader.SortField = "IndexNumber";
-					reportHeader.HeaderFieldType = ReportFieldType.Int;
-					break;
-
-				case HeaderMetadata.Series:
-					column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName);
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					reportHeader.SortField = "SeriesSortName,SortName";
-					break;
-
-				case HeaderMetadata.EpisodeSeries:
-					column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName);
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					itemId = (i) =>
-					{
-						Series series = this.GetObject<Episode, Series>(i, (x) => x.Series);
-						if (series == null)
-							return string.Empty;
-						return series.Id;
-					};
-					reportHeader.SortField = "SeriesSortName,SortName";
-					internalHeader = HeaderMetadata.Series;
-					break;
-
-				case HeaderMetadata.EpisodeSeason:
-					column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName);
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					itemId = (i) =>
-					{
-						Season season = this.GetObject<Episode, Season>(i, (x) => x.Season);
-						if (season == null)
-							return string.Empty;
-						return season.Id;
-					};
-					reportHeader.SortField = "SortName";
-					internalHeader = HeaderMetadata.Season;
-					break;
-
-				case HeaderMetadata.Network:
-					column = (i, r) => this.GetListAsString(i.Studios);
-					itemId = (i) => this.GetStudioID(i.Studios.FirstOrDefault());
-					reportHeader.ItemViewType = ItemViewType.ItemByNameDetails;
-					reportHeader.SortField = "Studio,SortName";
-					break;
-
-				case HeaderMetadata.Year:
-					column = (i, r) => this.GetSeriesProductionYear(i);
-					reportHeader.SortField = "ProductionYear,PremiereDate,SortName";
-					break;
-
-				case HeaderMetadata.ParentalRating:
-					column = (i, r) => i.OfficialRating;
-					reportHeader.SortField = "OfficialRating,SortName";
-					break;
-
-				case HeaderMetadata.CommunityRating:
-					column = (i, r) => i.CommunityRating;
-					reportHeader.SortField = "CommunityRating,SortName";
-					break;
-
-				case HeaderMetadata.Trailers:
-					column = (i, r) => this.GetBoolString(r.HasLocalTrailer);
-					reportHeader.ItemViewType = ItemViewType.TrailersImage;
-					break;
-
-				case HeaderMetadata.Specials:
-					column = (i, r) => this.GetBoolString(r.HasSpecials);
-					reportHeader.ItemViewType = ItemViewType.SpecialsImage;
-					break;
-
-				case HeaderMetadata.GameSystem:
-					column = (i, r) => this.GetObject<Game, string>(i, (x) => x.GameSystem);
-					reportHeader.SortField = "GameSystem,SortName";
-					break;
-
-				case HeaderMetadata.Players:
-					column = (i, r) => this.GetObject<Game, int?>(i, (x) => x.PlayersSupported);
-					reportHeader.SortField = "Players,GameSystem,SortName";
-					break;
-
-				case HeaderMetadata.AlbumArtist:
-					column = (i, r) => this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist);
-					itemId = (i) => this.GetPersonID(this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist));
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					reportHeader.SortField = "AlbumArtist,Album,SortName";
-
-					break;
-				case HeaderMetadata.MusicArtist:
-					column = (i, r) => this.GetObject<MusicArtist, string>(i, (x) => x.GetLookupInfo().Name);
-					reportHeader.ItemViewType = ItemViewType.Detail;
-					reportHeader.SortField = "AlbumArtist,Album,SortName";
-					internalHeader = HeaderMetadata.AlbumArtist;
-					break;
-				case HeaderMetadata.AudioAlbumArtist:
-					column = (i, r) => this.GetListAsString(this.GetObject<Audio, List<string>>(i, (x) => x.AlbumArtists));
-					reportHeader.SortField = "AlbumArtist,Album,SortName";
-					internalHeader = HeaderMetadata.AlbumArtist;
-					break;
-
-				case HeaderMetadata.AudioAlbum:
-					column = (i, r) => this.GetObject<Audio, string>(i, (x) => x.Album);
-					reportHeader.SortField = "Album,SortName";
-					internalHeader = HeaderMetadata.Album;
-					break;
-
-				case HeaderMetadata.Countries:
-					column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations));
-					break;
-
-				case HeaderMetadata.Disc:
-					column = (i, r) => i.ParentIndexNumber;
-					break;
-
-				case HeaderMetadata.Track:
-					column = (i, r) => i.IndexNumber;
-					break;
-
-				case HeaderMetadata.Tracks:
-					column = (i, r) => this.GetObject<MusicAlbum, List<Audio>>(i, (x) => x.Tracks.ToList(), new List<Audio>()).Count();
-					break;
-
-				case HeaderMetadata.Audio:
-					column = (i, r) => this.GetAudioStream(i);
-					break;
-
-				case HeaderMetadata.EmbeddedImage:
-					break;
-
-				case HeaderMetadata.Video:
-					column = (i, r) => this.GetVideoStream(i);
-					break;
-
-				case HeaderMetadata.Resolution:
-					column = (i, r) => this.GetVideoResolution(i);
-					break;
-
-				case HeaderMetadata.Subtitles:
-					column = (i, r) => this.GetBoolString(r.HasSubtitles);
-					reportHeader.ItemViewType = ItemViewType.SubtitleImage;
-					break;
-
-				case HeaderMetadata.Genres:
-					column = (i, r) => this.GetListAsString(i.Genres);
-					break;
-
-			}
-
-			string headerName = "";
-			if (internalHeader != HeaderMetadata.None)
-			{
-				string localHeader = "Header" + internalHeader.ToString();
-				headerName = internalHeader != HeaderMetadata.None ? ReportHelper.GetJavaScriptLocalizedString(localHeader) : "";
-				if (string.Compare(localHeader, headerName, StringComparison.CurrentCultureIgnoreCase) == 0)
-					headerName = ReportHelper.GetServerLocalizedString(localHeader);
-			}
-
-			reportHeader.Name = headerName;
-			reportHeader.FieldName = header;
-			ReportOptions<BaseItem> option = new ReportOptions<BaseItem>()
-			{
-				Header = reportHeader,
-				Column = column,
-				ItemID = itemId
-			};
-			return option;
-		}
-	}
+            }
+
+        }
+
+        /// <summary> Gets report option. </summary>
+        /// <param name="header"> The header. </param>
+        /// <param name="sortField"> The sort field. </param>
+        /// <returns> The report option. </returns>
+        private ReportOptions<BaseItem> GetOption(HeaderMetadata header, string sortField = "")
+        {
+            HeaderMetadata internalHeader = header;
+
+            ReportOptions<BaseItem> option = new ReportOptions<BaseItem>()
+            {
+                Header = new ReportHeader
+                {
+                    HeaderFieldType = ReportFieldType.String,
+                    SortField = sortField,
+                    Type = "",
+                    ItemViewType = ItemViewType.None
+                }
+            };
+
+            switch (header)
+            {
+                case HeaderMetadata.StatusImage:
+                    option.Header.ItemViewType = ItemViewType.StatusImage;
+                    internalHeader = HeaderMetadata.Status;
+                    option.Header.CanGroup = false;
+                    break;
+
+                case HeaderMetadata.Name:
+                    option.Column = (i, r) => i.Name;
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.Header.SortField = "SortName";
+                    break;
+
+                case HeaderMetadata.DateAdded:
+                    option.Column = (i, r) => i.DateCreated;
+                    option.Header.SortField = "DateCreated,SortName";
+                    option.Header.HeaderFieldType = ReportFieldType.DateTime;
+                    option.Header.Type = "";
+                    break;
+
+                case HeaderMetadata.PremiereDate:
+                case HeaderMetadata.ReleaseDate:
+                    option.Column = (i, r) => i.PremiereDate;
+                    option.Header.HeaderFieldType = ReportFieldType.DateTime;
+                    option.Header.SortField = "ProductionYear,PremiereDate,SortName";
+                    break;
+
+                case HeaderMetadata.Runtime:
+                    option.Column = (i, r) => this.GetRuntimeDateTime(i.RunTimeTicks);
+                    option.Header.HeaderFieldType = ReportFieldType.Minutes;
+                    option.Header.SortField = "Runtime,SortName";
+                    break;
+
+                case HeaderMetadata.PlayCount:
+                    option.Header.HeaderFieldType = ReportFieldType.Int;
+                    break;
+
+                case HeaderMetadata.Season:
+                    option.Column = (i, r) => this.GetEpisode(i);
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.Header.SortField = "SortName";
+                    break;
+
+                case HeaderMetadata.SeasonNumber:
+                    option.Column = (i, r) => this.GetObject<Season, string>(i, (x) => x.IndexNumber == null ? "" : x.IndexNumber.ToString());
+                    option.Header.SortField = "IndexNumber";
+                    option.Header.HeaderFieldType = ReportFieldType.Int;
+                    break;
+
+                case HeaderMetadata.Series:
+                    option.Column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName);
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.Header.SortField = "SeriesSortName,SortName";
+                    break;
+
+                case HeaderMetadata.EpisodeSeries:
+                    option.Column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName);
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.ItemID = (i) =>
+                    {
+                        Series series = this.GetObject<Episode, Series>(i, (x) => x.Series);
+                        if (series == null)
+                            return string.Empty;
+                        return series.Id;
+                    };
+                    option.Header.SortField = "SeriesSortName,SortName";
+                    internalHeader = HeaderMetadata.Series;
+                    break;
+
+                case HeaderMetadata.EpisodeSeason:
+                    option.Column = (i, r) => this.GetObject<IHasSeries, string>(i, (x) => x.SeriesName);
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.ItemID = (i) =>
+                    {
+                        Season season = this.GetObject<Episode, Season>(i, (x) => x.Season);
+                        if (season == null)
+                            return string.Empty;
+                        return season.Id;
+                    };
+                    option.Header.SortField = "SortName";
+                    internalHeader = HeaderMetadata.Season;
+                    break;
+
+                case HeaderMetadata.Network:
+                    option.Column = (i, r) => this.GetListAsString(i.Studios);
+                    option.ItemID = (i) => this.GetStudioID(i.Studios.FirstOrDefault());
+                    option.Header.ItemViewType = ItemViewType.ItemByNameDetails;
+                    option.Header.SortField = "Studio,SortName";
+                    break;
+
+                case HeaderMetadata.Year:
+                    option.Column = (i, r) => this.GetSeriesProductionYear(i);
+                    option.Header.SortField = "ProductionYear,PremiereDate,SortName";
+                    break;
+
+                case HeaderMetadata.ParentalRating:
+                    option.Column = (i, r) => i.OfficialRating;
+                    option.Header.SortField = "OfficialRating,SortName";
+                    break;
+
+                case HeaderMetadata.CommunityRating:
+                    option.Column = (i, r) => i.CommunityRating;
+                    option.Header.SortField = "CommunityRating,SortName";
+                    break;
+
+                case HeaderMetadata.Trailers:
+                    option.Column = (i, r) => this.GetBoolString(r.HasLocalTrailer);
+                    option.Header.ItemViewType = ItemViewType.TrailersImage;
+                    break;
+
+                case HeaderMetadata.Specials:
+                    option.Column = (i, r) => this.GetBoolString(r.HasSpecials);
+                    option.Header.ItemViewType = ItemViewType.SpecialsImage;
+                    break;
+
+                case HeaderMetadata.GameSystem:
+                    option.Column = (i, r) => this.GetObject<Game, string>(i, (x) => x.GameSystem);
+                    option.Header.SortField = "GameSystem,SortName";
+                    break;
+
+                case HeaderMetadata.Players:
+                    option.Column = (i, r) => this.GetObject<Game, int?>(i, (x) => x.PlayersSupported);
+                    option.Header.SortField = "Players,GameSystem,SortName";
+                    break;
+
+                case HeaderMetadata.AlbumArtist:
+                    option.Column = (i, r) => this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist);
+                    option.ItemID = (i) => this.GetPersonID(this.GetObject<MusicAlbum, string>(i, (x) => x.AlbumArtist));
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.Header.SortField = "AlbumArtist,Album,SortName";
+
+                    break;
+                case HeaderMetadata.MusicArtist:
+                    option.Column = (i, r) => this.GetObject<MusicArtist, string>(i, (x) => x.GetLookupInfo().Name);
+                    option.Header.ItemViewType = ItemViewType.Detail;
+                    option.Header.SortField = "AlbumArtist,Album,SortName";
+                    internalHeader = HeaderMetadata.AlbumArtist;
+                    break;
+                case HeaderMetadata.AudioAlbumArtist:
+                    option.Column = (i, r) => this.GetListAsString(this.GetObject<Audio, List<string>>(i, (x) => x.AlbumArtists));
+                    option.Header.SortField = "AlbumArtist,Album,SortName";
+                    internalHeader = HeaderMetadata.AlbumArtist;
+                    break;
+
+                case HeaderMetadata.AudioAlbum:
+                    option.Column = (i, r) => this.GetObject<Audio, string>(i, (x) => x.Album);
+                    option.Header.SortField = "Album,SortName";
+                    internalHeader = HeaderMetadata.Album;
+                    break;
+
+                case HeaderMetadata.Countries:
+                    option.Column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations));
+                    break;
+
+                case HeaderMetadata.Disc:
+                    option.Column = (i, r) => i.ParentIndexNumber;
+                    break;
+
+                case HeaderMetadata.Track:
+                    option.Column = (i, r) => i.IndexNumber;
+                    break;
+
+                case HeaderMetadata.Tracks:
+                    option.Column = (i, r) => this.GetObject<MusicAlbum, List<Audio>>(i, (x) => x.Tracks.ToList(), new List<Audio>()).Count();
+                    break;
+
+                case HeaderMetadata.Audio:
+                    option.Column = (i, r) => this.GetAudioStream(i);
+                    break;
+
+                case HeaderMetadata.EmbeddedImage:
+                    break;
+
+                case HeaderMetadata.Video:
+                    option.Column = (i, r) => this.GetVideoStream(i);
+                    break;
+
+                case HeaderMetadata.Resolution:
+                    option.Column = (i, r) => this.GetVideoResolution(i);
+                    break;
+
+                case HeaderMetadata.Subtitles:
+                    option.Column = (i, r) => this.GetBoolString(r.HasSubtitles);
+                    option.Header.ItemViewType = ItemViewType.SubtitleImage;
+                    break;
+
+                case HeaderMetadata.Genres:
+                    option.Column = (i, r) => this.GetListAsString(i.Genres);
+                    break;
+
+            }
+
+            option.Header.Name = GetLocalizedHeader(internalHeader);
+            option.Header.FieldName = header;
+
+            return option;
+        }
+
+        /// <summary> Gets report rows. </summary>
+        /// <param name="items"> The items. </param>
+        /// <param name="options"> Options for controlling the operation. </param>
+        /// <returns> The report rows. </returns>
+        private List<ReportRow> GetReportRows(IEnumerable<BaseItem> items, List<ReportOptions<BaseItem>> options)
+        {
+            var rows = new List<ReportRow>();
+
+            foreach (BaseItem item in items)
+            {
+                ReportRow rRow = GetRow(item);
+                foreach (ReportOptions<BaseItem> option in options)
+                {
+                    object itemColumn = option.Column != null ? option.Column(item, rRow) : "";
+                    object itemId = option.ItemID != null ? option.ItemID(item) : "";
+                    ReportItem rItem = new ReportItem
+                    {
+                        Name = ReportHelper.ConvertToString(itemColumn, option.Header.HeaderFieldType),
+                        Id = ReportHelper.ConvertToString(itemId, ReportFieldType.Object)
+                    };
+                    rRow.Columns.Add(rItem);
+                }
+
+                rows.Add(rRow);
+            }
+
+            return rows;
+        }
+
+        /// <summary> Gets a row. </summary>
+        /// <param name="item"> The item. </param>
+        /// <returns> The row. </returns>
+        private ReportRow GetRow(BaseItem item)
+        {
+            var hasTrailers = item as IHasTrailers;
+            var hasSpecialFeatures = item as IHasSpecialFeatures;
+            var video = item as Video;
+            ReportRow rRow = new ReportRow
+            {
+                Id = item.Id.ToString("N"),
+                HasLockData = item.IsLocked,
+                IsUnidentified = item.IsUnidentified,
+                HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false,
+                HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0),
+                HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0),
+                HasImageTagsLogo = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Logo) > 0),
+                HasSpecials = hasSpecialFeatures != null ? hasSpecialFeatures.SpecialFeatureIds.Count > 0 : false,
+                HasSubtitles = video != null ? video.HasSubtitles : false,
+                RowType = ReportHelper.GetRowType(item.GetClientTypeName())
+            };
+            return rRow;
+        }
+
+        #endregion
+
+    }
 }

+ 2 - 1
MediaBrowser.Api/Reports/Data/ReportOptions.cs

@@ -7,8 +7,9 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Reports
 {
+
 	/// <summary> A report options. </summary>
-	internal class ReportOptions<I>
+	public class ReportOptions<I>
 	{
 		/// <summary> Initializes a new instance of the ReportOptions class. </summary>
 		public ReportOptions()

+ 0 - 0
MediaBrowser.Api/Reports/Data/ReportGroup.cs → MediaBrowser.Api/Reports/Model/ReportGroup.cs


+ 0 - 0
MediaBrowser.Api/Reports/Data/ReportHeader.cs → MediaBrowser.Api/Reports/Model/ReportHeader.cs


+ 0 - 0
MediaBrowser.Api/Reports/Data/ReportItem.cs → MediaBrowser.Api/Reports/Model/ReportItem.cs


+ 0 - 0
MediaBrowser.Api/Reports/Data/ReportResult.cs → MediaBrowser.Api/Reports/Model/ReportResult.cs


+ 1 - 1
MediaBrowser.Api/Reports/Data/ReportRow.cs → MediaBrowser.Api/Reports/Model/ReportRow.cs

@@ -66,6 +66,6 @@ namespace MediaBrowser.Api.Reports
 
 		/// <summary> Gets or sets the type. </summary>
 		/// <value> The type. </value>
-		public ReportViewType RowType { get; set; }
+		public ReportIncludeItemTypes RowType { get; set; }
 	}
 }

+ 146 - 10
MediaBrowser.Api/Reports/ReportRequests.cs

@@ -7,8 +7,80 @@ using System.Linq;
 
 namespace MediaBrowser.Api.Reports
 {
-    public class BaseReportRequest : BaseItemsRequest
-	{
+    public interface IReportsDownload : IReportsQuery
+    {
+        /// <summary> Gets or sets the minimum date. </summary>
+        /// <value> The minimum date. </value>
+        string MinDate { get; set; }
+    }
+
+    /// <summary> Interface for reports query. </summary>
+    public interface IReportsQuery : IReportsHeader
+    {
+        /// <summary>
+        /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.GetActivityLogs has
+        /// query limit. </summary>
+        /// <value>
+        /// true if this MediaBrowser.Api.Reports.GetActivityLogs has query limit, false if not. </value>
+        bool HasQueryLimit { get; set; }
+        /// <summary> Gets or sets who group this MediaBrowser.Api.Reports.GetActivityLogs. </summary>
+        /// <value> Describes who group this MediaBrowser.Api.Reports.GetActivityLogs. </value>
+        string GroupBy { get; set; }
+
+        /// <summary>
+        /// Skips over a given number of items within the results. Use for paging.
+        /// </summary>
+        /// <value>The start index.</value>
+        int? StartIndex { get; set; }
+        /// <summary>
+        /// The maximum number of items to return
+        /// </summary>
+        /// <value>The limit.</value>
+        int? Limit { get; set; }
+
+    }
+    public interface IReportsHeader
+    {
+        /// <summary> Gets or sets the report view. </summary>
+        /// <value> The report view. </value>
+        string ReportView { get; set; }
+
+        /// <summary> Gets or sets the report columns. </summary>
+        /// <value> The report columns. </value>
+        string ReportColumns { get; set; }
+
+        /// <summary> Gets or sets a list of types of the include items. </summary>
+        /// <value> A list of types of the include items. </value>
+        string IncludeItemTypes { get; set; }
+
+    }
+
+    public class BaseReportRequest : BaseItemsRequest, IReportsQuery
+    {
+        /// <summary> Gets or sets the report view. </summary>
+        /// <value> The report view. </value>
+        [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ReportView { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.BaseReportRequest has
+        /// query limit. </summary>
+        /// <value>
+        /// true if this MediaBrowser.Api.Reports.BaseReportRequest has query limit, false if not. </value>
+        [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; }
+
+        /// <summary>
+        /// Gets or sets who group this MediaBrowser.Api.Reports.BaseReportRequest. </summary>
+        /// <value> Describes who group this MediaBrowser.Api.Reports.BaseReportRequest. </value>
+        [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; }
+
+        /// <summary> Gets or sets the report columns. </summary>
+        /// <value> The report columns. </value>
+        [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ReportColumns { get; set; }
+
         /// <summary>
         /// Gets or sets the user id.
         /// </summary>
@@ -63,11 +135,6 @@ namespace MediaBrowser.Api.Reports
         [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; }
         
-        public bool HasQueryLimit { get; set; }
-		public string GroupBy { get; set; }
-
-		public string ReportColumns { get; set; }
-
         /// <summary>
         /// Gets or sets the video types.
         /// </summary>
@@ -261,8 +328,22 @@ namespace MediaBrowser.Api.Reports
 	}
 
 	[Route("/Reports/Headers", "GET", Summary = "Gets reports headers based on library items")]
-	public class GetReportHeaders : BaseReportRequest, IReturn<List<ReportHeader>>
-	{
+    public class GetReportHeaders : IReturn<List<ReportHeader>>, IReportsHeader
+    {
+        /// <summary> Gets or sets the report view. </summary>
+        /// <value> The report view. </value>
+        [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ReportView { get; set; }
+
+        /// <summary> Gets or sets a list of types of the include items. </summary>
+        /// <value> A list of types of the include items. </value>
+        [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; }
+
+        /// <summary> Gets or sets the report columns. </summary>
+        /// <value> The report columns. </value>
+        [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 +354,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 +362,61 @@ namespace MediaBrowser.Api.Reports
 		}
 
 		public ReportExportType ExportType { get; set; }
+
+        /// <summary> Gets or sets the minimum date. </summary>
+        /// <value> The minimum date. </value>
+        [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<ReportResult>, IReportsQuery, IReportsDownload
+    {
+        /// <summary> Gets or sets the report view. </summary>
+        /// <value> The report view. </value>
+        [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ReportView { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this MediaBrowser.Api.Reports.GetActivityLogs has
+        /// query limit. </summary>
+        /// <value>
+        /// true if this MediaBrowser.Api.Reports.GetActivityLogs has query limit, false if not. </value>
+        [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; }
+
+        /// <summary> Gets or sets who group this MediaBrowser.Api.Reports.GetActivityLogs. </summary>
+        /// <value> Describes who group this MediaBrowser.Api.Reports.GetActivityLogs. </value>
+        [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; }
+
+        /// <summary> Gets or sets the report columns. </summary>
+        /// <value> The report columns. </value>
+        [ApiMember(Name = "ReportColumns", Description = "Optional. The columns to show.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ReportColumns { get; set; }
+
+        /// <summary>
+        /// Skips over a given number of items within the results. Use for paging.
+        /// </summary>
+        /// <value>The start index.</value>
+        [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; }
+
+        /// <summary>
+        /// The maximum number of items to return
+        /// </summary>
+        /// <value>The limit.</value>
+        [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; }
+
+        /// <summary> Gets or sets the minimum date. </summary>
+        /// <value> The minimum date. </value>
+        [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; }
+    }
 }

+ 1214 - 1134
MediaBrowser.Api/Reports/ReportsService.cs

@@ -24,1139 +24,1219 @@ using System.Text;
 
 namespace MediaBrowser.Api.Reports
 {
-	/// <summary> The reports service. </summary>
-	/// <seealso cref="T:MediaBrowser.Api.BaseApiService"/>
-	public class ReportsService : BaseApiService
-	{
-
-
-		/// <summary> Manager for user. </summary>
-		private readonly IUserManager _userManager;
-
-		/// <summary> Manager for library. </summary>
-		private readonly ILibraryManager _libraryManager;
-		/// <summary> The localization. </summary>
-		private readonly ILocalizationManager _localization;
-
-		/// <summary>
-		/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class. </summary>
-		/// <param name="userManager"> Manager for user. </param>
-		/// <param name="libraryManager"> Manager for library. </param>
-		/// <param name="localization"> The localization. </param>
-		public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization)
-		{
-			_userManager = userManager;
-			_libraryManager = libraryManager;
-			_localization = localization;
-		}
-
-		/// <summary> Gets the given request. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> A Task&lt;object&gt; </returns>
-		public async Task<object> 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);
-
-		}
-
-		/// <summary> Gets the given request. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> A Task&lt;object&gt; </returns>
-		public async Task<object> Get(GetItemReport request)
-		{
-			if (string.IsNullOrEmpty(request.IncludeItemTypes))
-				return null;
-
-			var reportResult = await GetReportResult(request);
-
-			return ToOptimizedResult(reportResult);
-		}
-
-		/// <summary> Gets the given request. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> A Task&lt;object&gt; </returns>
-		public async Task<object> Get(GetReportDownload request)
-		{
-			if (string.IsNullOrEmpty(request.IncludeItemTypes))
-				return null;
-
-			var headers = new Dictionary<string, string>();
-			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<BaseItem> 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;
-		}
-
-		/// <summary> Gets the given request. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> A Task&lt;object&gt; </returns>
-		public async Task<object> Get(GetReportStatistics request)
-		{
-			if (string.IsNullOrEmpty(request.IncludeItemTypes))
-				return null;
-			var reportResult = await GetReportStatistic(request);
-
-			return ToOptimizedResult(reportResult);
-		}
-
-		/// <summary> Gets report statistic. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> The report statistic. </returns>
-		private async Task<ReportStatResult> GetReportStatistic(GetReportStatistics request)
-		{
-			ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
-			QueryResult<BaseItem> 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;
-		}
-
-		/// <summary> Gets report result. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> The report result. </returns>
-		private async Task<ReportResult> GetReportResult(GetItemReport request)
-		{
-
-			ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
-			ReportBuilder reportBuilder = new ReportBuilder(_libraryManager);
-			QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false);
-			ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request);
-			reportResult.TotalRecordCount = queryResult.TotalRecordCount;
-
-			return reportResult;
-		}
-
-		/// <summary> Gets query result. </summary>
-		/// <param name="request"> The request. </param>
-		/// <returns> The query result. </returns>
-		private async Task<QueryResult<BaseItem>> 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<BaseItem> 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<BaseItem> { Items = items.ToArray() };
-
-		}
-
-		/// <summary> Gets items query. </summary>
-		/// <param name="request"> The request. </param>
-		/// <param name="user"> The user. </param>
-		/// <returns> The items query. </returns>
-		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;
-		}
-
-		/// <summary> Applies filtering. </summary>
-		/// <param name="items"> The items. </param>
-		/// <param name="filter"> The filter. </param>
-		/// <param name="user"> The user. </param>
-		/// <param name="repository"> The repository. </param>
-		/// <returns> IEnumerable{BaseItem}. </returns>
-		internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> 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;
-		}
-
-		/// <summary> Applies the additional filters. </summary>
-		/// <param name="request"> The request. </param>
-		/// <param name="i"> Zero-based index of the. </param>
-		/// <param name="user"> The user. </param>
-		/// <param name="isPreFiltered"> true if this object is pre filtered. </param>
-		/// <param name="libraryManager"> Manager for library. </param>
-		/// <returns> true if it succeeds, false if it fails. </returns>
+    /// <summary> The reports service. </summary>
+    /// <seealso cref="T:MediaBrowser.Api.BaseApiService"/>
+    public class ReportsService : BaseApiService
+    {
+        #region [Constructors]
+
+        /// <summary>
+        /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class. </summary>
+        /// <param name="userManager"> Manager for user. </param>
+        /// <param name="libraryManager"> Manager for library. </param>
+        /// <param name="localization"> The localization. </param>
+        /// <param name="activityManager"> Manager for activity. </param>
+        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
+
+        /// <summary> Manager for library. </summary>
+        private readonly ILibraryManager _libraryManager;   ///< Manager for library
+        /// <summary> The localization. </summary>
+
+        private readonly ILocalizationManager _localization;    ///< The localization
+
+        private readonly IActivityRepository _repo;
+
+        /// <summary> Manager for user. </summary>
+        private readonly IUserManager _userManager; ///< Manager for user
+
+        #endregion
+
+        #region [Public Methods]
+
+        /// <summary> Gets the given request. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> A Task&lt;object&gt; </returns>
+        public async Task<object> Get(GetActivityLogs request)
+        {
+            ReportResult result = await GetReportActivities(request).ConfigureAwait(false);
+            return ToOptimizedResult(result);
+        }
+
+        /// <summary> Gets the given request. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> A Task&lt;object&gt; </returns>
+        public async Task<object> Get(GetReportHeaders request)
+        {
+            if (string.IsNullOrEmpty(request.IncludeItemTypes))
+                return null;
+
+            ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView);
+            ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
+
+            List<ReportHeader> result = new List<ReportHeader>();
+            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);
+
+        }
+
+        /// <summary> Gets the given request. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> A Task&lt;object&gt; </returns>
+        public async Task<object> Get(GetItemReport request)
+        {
+            if (string.IsNullOrEmpty(request.IncludeItemTypes))
+                return null;
+
+            var reportResult = await GetReportResult(request);
+
+            return ToOptimizedResult(reportResult);
+        }
+
+        /// <summary> Gets the given request. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> A Task&lt;object&gt; </returns>
+        public async Task<object> Get(GetReportStatistics request)
+        {
+            if (string.IsNullOrEmpty(request.IncludeItemTypes))
+                return null;
+            var reportResult = await GetReportStatistic(request);
+
+            return ToOptimizedResult(reportResult);
+        }
+
+        /// <summary> Gets the given request. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> A Task&lt;object&gt; </returns>
+        public async Task<object> Get(GetReportDownload request)
+        {
+            if (string.IsNullOrEmpty(request.IncludeItemTypes))
+                return null;
+
+            ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView);
+            var headers = new Dictionary<string, string>();
+            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<BaseItem> 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 [Internal Methods]
+
+        /// <summary> Applies filtering. </summary>
+        /// <param name="items"> The items. </param>
+        /// <param name="filter"> The filter. </param>
+        /// <param name="user"> The user. </param>
+        /// <param name="repository"> The repository. </param>
+        /// <returns> IEnumerable{BaseItem}. </returns>
+        internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> 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;
+        }
+
+        #endregion
+
+        #region [Private Methods]
+
+        /// <summary> Applies the additional filters. </summary>
+        /// <param name="request"> The request. </param>
+        /// <param name="i"> Zero-based index of the. </param>
+        /// <param name="user"> The user. </param>
+        /// <param name="isPreFiltered"> true if this object is pre filtered. </param>
+        /// <param name="libraryManager"> Manager for library. </param>
+        /// <returns> true if it succeeds, false if it fails. </returns>
         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<BoxSet>().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<IHasAlbumArtist>()
-					.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<Series>().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<Series>().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;
-		}
-
-		/// <summary> Applies the paging. </summary>
-		/// <param name="request"> The request. </param>
-		/// <param name="items"> The items. </param>
-		/// <returns> IEnumerable{BaseItem}. </returns>
-		private IEnumerable<BaseItem> ApplyPaging(GetItems request, IEnumerable<BaseItem> 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;
-		}
-
-	}
+        {
+            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<BoxSet>().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 => i.People.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 (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase))))
+                        {
+                            return false;
+                        }
+                    }
+                    else
+                    {
+                        var types = personTypes;
+
+                        var ok = new[] { i }.Any(item =>
+                                item.People != null &&
+                                item.People.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<IHasAlbumArtist>()
+                    .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<Series>().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<Series>().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;
+        }
+
+        /// <summary> Applies the paging. </summary>
+        /// <param name="request"> The request. </param>
+        /// <param name="items"> The items. </param>
+        /// <returns> IEnumerable{BaseItem}. </returns>
+        private IEnumerable<BaseItem> ApplyPaging(GetItems request, IEnumerable<BaseItem> 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;
+        }
+
+        /// <summary> Gets items query. </summary>
+        /// <param name="request"> The request. </param>
+        /// <param name="user"> The user. </param>
+        /// <returns> The items query. </returns>
+        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;
+        }
+
+        /// <summary> Gets query result. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> The query result. </returns>
+        private async Task<QueryResult<BaseItem>> 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<BaseItem> 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<BaseItem> { Items = items.ToArray() };
+
+        }
+
+        /// <summary> Gets report activities. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> The report activities. </returns>
+        private Task<ReportResult> GetReportActivities(IReportsDownload request)
+        {
+            return Task<ReportResult>.Run(() =>
+            {
+                DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ?
+                (DateTime?)null :
+                DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
+                var queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, request.Limit);
+                //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;
+
+            });
+
+        }
+
+        /// <summary> Gets report result. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> The report result. </returns>
+        private async Task<ReportResult> GetReportResult(GetItemReport request)
+        {
+            ReportBuilder reportBuilder = new ReportBuilder(_libraryManager);
+            QueryResult<BaseItem> queryResult = await GetQueryResult(request).ConfigureAwait(false);
+            ReportResult reportResult = reportBuilder.GetResult(queryResult.Items, request);
+            reportResult.TotalRecordCount = queryResult.TotalRecordCount;
+
+            return reportResult;
+        }
+
+        /// <summary> Gets report statistic. </summary>
+        /// <param name="request"> The request. </param>
+        /// <returns> The report statistic. </returns>
+        private async Task<ReportStatResult> GetReportStatistic(GetReportStatistics request)
+        {
+            ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
+            QueryResult<BaseItem> 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
+
+    }
 }

+ 262 - 202
MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs

@@ -9,206 +9,266 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Reports
 {
-	/// <summary> A report stat builder. </summary>
-	/// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
-	public class ReportStatBuilder : ReportBuilderBase
-	{
-		/// <summary>
-		/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. </summary>
-		/// <param name="libraryManager"> Manager for library. </param>
-		public ReportStatBuilder(ILibraryManager libraryManager)
-			: base(libraryManager)
-		{
-		}
-
-		/// <summary> Gets report stat result. </summary>
-		/// <param name="items"> The items. </param>
-		/// <param name="reportRowType"> Type of the report row. </param>
-		/// <param name="topItem"> The top item. </param>
-		/// <returns> The report stat result. </returns>
-		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<string> t = new List<string> { 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<IHasProductionLocations>()
-						.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;
-		}
-
-
-		/// <summary> Gets the groups. </summary>
-		/// <param name="result"> The result. </param>
-		/// <param name="header"> The header. </param>
-		/// <param name="topItem"> The top item. </param>
-		/// <param name="top"> The top. </param>
-		private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable<ReportStatItem> top)
-		{
-			if (top.Count() > 0)
-			{
-				var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) };
-				group.Items.AddRange(top);
-				result.Groups.Add(group);
-			}
-		}
-	}
+    /// <summary> A report stat builder. </summary>
+    /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
+    public class ReportStatBuilder : ReportBuilderBase
+    {
+        #region [Constructors]
+
+        /// <summary>
+        /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. </summary>
+        /// <param name="libraryManager"> Manager for library. </param>
+        public ReportStatBuilder(ILibraryManager libraryManager)
+            : base(libraryManager)
+        {
+        }
+
+        #endregion
+
+        #region [Public Methods]
+
+        /// <summary> Gets report stat result. </summary>
+        /// <param name="items"> The items. </param>
+        /// <param name="reportIncludeItemTypes"> List of types of the report include items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The report stat result. </returns>
+        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]
+        /// <summary> Gets the headers. </summary>
+        /// <typeparam name="H"> Type of the header. </typeparam>
+        /// <param name="request"> The request. </param>
+        /// <returns> The headers. </returns>
+        /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/>
+        protected internal override List<ReportHeader> GetHeaders<H>(H request)
+        {
+            throw new NotImplementedException();
+        }
+
+        #endregion
+
+        #region [Private Methods]
+
+        /// <summary> Gets the groups. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="header"> The header. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <param name="top"> The top. </param>
+        private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable<ReportStatItem> top)
+        {
+            if (top != null && top.Count() > 0)
+            {
+                var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) };
+                group.Items.AddRange(top);
+                result.Groups.Add(group);
+            }
+        }
+
+        /// <summary> Gets resul production locations. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The resul production locations. </returns>
+        private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5)
+        {
+            this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderCountries"), topItem,
+                        items.OfType<IHasProductionLocations>()
+                        .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;
+        }
+
+        /// <summary> Gets result community ratings. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The result community ratings. </returns>
+        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;
+        }
+
+        /// <summary> Gets result genres. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The result genres. </returns>
+        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;
+
+        }
+
+        /// <summary> Gets result parental ratings. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The result parental ratings. </returns>
+        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;
+        }
+
+        /// <summary> Gets result persons. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The result persons. </returns>
+        private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5)
+        {
+            List<string> t = new List<string> { PersonType.Actor, PersonType.Composer, PersonType.Director, PersonType.GuestStar, PersonType.Producer, PersonType.Writer, "Artist", "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)
+                                .GroupBy(x => x.Name)
+                                .OrderByDescending(x => x.Count())
+                                .Take(topItem);
+                if (ps != null && ps.Count() > 0)
+                    this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem,
+                            ps.Select(x => new ReportStatItem
+                                    {
+                                        Name = x.Key,
+                                        Value = x.Count().ToString(),
+                                        Id = GetPersonID(x.Key)
+                                    })
+                    );
+            }
+
+            return result;
+        }
+
+        /// <summary> Gets result production years. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The result production years. </returns>
+        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;
+        }
+
+        /// <summary> Gets result studios. </summary>
+        /// <param name="result"> The result. </param>
+        /// <param name="items"> The items. </param>
+        /// <param name="topItem"> The top item. </param>
+        /// <returns> The result studios. </returns>
+        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;
+
+        }
+
+        #endregion
+
+    }
 }

+ 6 - 1
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -1468,5 +1468,10 @@
     "HeaderNavigation": "Navigation",
     "LegendTheseSettingsShared": "These settings are shared on all devices",
     "OptionEnableAutomaticServerUpdates": "Enable automatic server updates",
-    "OptionOtherTrailers": "Include trailers from older movies"
+    "OptionOtherTrailers": "Include trailers from older movies",
+    "HeaderOverview": "Overview",
+    "HeaderShortOverview": "Short Overview",
+    "HeaderType": "Type",
+    "HeaderSeverity": "Severity",
+    "OptionReportActivities": "Activities Log"
 }