using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { /// A report builder. /// public class ReportBuilder : ReportBuilderBase { #region [Constructors] /// /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportBuilder class. /// Manager for library. public ReportBuilder(ILibraryManager libraryManager) : base(libraryManager) { } #endregion #region [Public Methods] /// Gets report result. /// The items. /// The request. /// The report result. public ReportResult GetResult(BaseItem[] items, IReportsQuery request) { ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); ReportDisplayType displayType = ReportHelper.GetReportDisplayType(request.DisplayType); List> options = this.GetReportOptions(request, () => this.GetDefaultHeaderMetadata(reportRowType), (hm) => this.GetOption(hm)).Where(x => this.DisplayTypeVisible(x.Header.DisplayType, displayType)).ToList(); var headers = GetHeaders(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] /// Gets the headers. /// Type of the header. /// The request. /// The headers. /// protected internal override List GetHeaders(H request) { ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); return this.GetHeaders(request, () => this.GetDefaultHeaderMetadata(reportRowType), (hm) => this.GetOption(hm)); } #endregion #region [Private Methods] /// Gets default report header metadata. /// Type of the report row. /// The default report header metadata. private List GetDefaultHeaderMetadata(ReportIncludeItemTypes reportIncludeItemTypes) { switch (reportIncludeItemTypes) { case ReportIncludeItemTypes.Season: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Series, HeaderMetadata.Season, HeaderMetadata.SeasonNumber, HeaderMetadata.DateAdded, HeaderMetadata.Year, HeaderMetadata.Genres }; case ReportIncludeItemTypes.Series: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.Network, HeaderMetadata.DateAdded, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Runtime, HeaderMetadata.Trailers, HeaderMetadata.Specials }; case ReportIncludeItemTypes.MusicAlbum: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.AlbumArtist, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Tracks, HeaderMetadata.Year, HeaderMetadata.Genres }; case ReportIncludeItemTypes.MusicArtist: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.MusicArtist, HeaderMetadata.Countries, HeaderMetadata.DateAdded, HeaderMetadata.Year, HeaderMetadata.Genres }; case ReportIncludeItemTypes.Game: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.GameSystem, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Players, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.Trailers }; case ReportIncludeItemTypes.Movie: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Runtime, HeaderMetadata.Video, HeaderMetadata.Resolution, HeaderMetadata.Audio, HeaderMetadata.Subtitles, HeaderMetadata.Trailers, HeaderMetadata.Specials }; case ReportIncludeItemTypes.Book: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating }; case ReportIncludeItemTypes.BoxSet: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Trailers }; case ReportIncludeItemTypes.Audio: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.AudioAlbumArtist, HeaderMetadata.AudioAlbum, HeaderMetadata.Disc, HeaderMetadata.Track, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Runtime, HeaderMetadata.Audio }; case ReportIncludeItemTypes.Episode: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.EpisodeSeries, HeaderMetadata.Season, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Runtime, HeaderMetadata.Video, HeaderMetadata.Resolution, HeaderMetadata.Audio, HeaderMetadata.Subtitles, HeaderMetadata.Trailers, HeaderMetadata.Specials }; case ReportIncludeItemTypes.Video: case ReportIncludeItemTypes.MusicVideo: case ReportIncludeItemTypes.Trailer: case ReportIncludeItemTypes.BaseItem: default: return new List { HeaderMetadata.Status, HeaderMetadata.Locked, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, HeaderMetadata.Name, HeaderMetadata.DateAdded, HeaderMetadata.ReleaseDate, HeaderMetadata.Year, HeaderMetadata.Genres, HeaderMetadata.ParentalRating, HeaderMetadata.CommunityRating, HeaderMetadata.Runtime, HeaderMetadata.Video, HeaderMetadata.Resolution, HeaderMetadata.Audio, HeaderMetadata.Subtitles, HeaderMetadata.Trailers, HeaderMetadata.Specials }; } } /// Gets report option. /// The header. /// The sort field. /// The report option. private ReportOptions GetOption(HeaderMetadata header, string sortField = "") { HeaderMetadata internalHeader = header; ReportOptions option = new ReportOptions() { Header = new ReportHeader { HeaderFieldType = ReportFieldType.String, SortField = sortField, Type = "", ItemViewType = ItemViewType.None } }; switch (header) { case HeaderMetadata.Status: option.Header.ItemViewType = ItemViewType.StatusImage; internalHeader = HeaderMetadata.Status; option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Screen; break; case HeaderMetadata.Locked: option.Column = (i, r) => this.GetBoolString(r.HasLockData); option.Header.ItemViewType = ItemViewType.LockDataImage; option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Export; break; case HeaderMetadata.Unidentified: option.Column = (i, r) => this.GetBoolString(r.IsUnidentified); option.Header.ItemViewType = ItemViewType.UnidentifiedImage; option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Export; break; case HeaderMetadata.ImagePrimary: option.Column = (i, r) => this.GetBoolString(r.HasImageTagsPrimary); option.Header.ItemViewType = ItemViewType.TagsPrimaryImage; option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Export; break; case HeaderMetadata.ImageBackdrop: option.Column = (i, r) => this.GetBoolString(r.HasImageTagsBackdrop); option.Header.ItemViewType = ItemViewType.TagsBackdropImage; option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Export; break; case HeaderMetadata.ImageLogo: option.Column = (i, r) => this.GetBoolString(r.HasImageTagsLogo); option.Header.ItemViewType = ItemViewType.TagsLogoImage; option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Export; 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(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(i, (x) => x.SeriesName); option.Header.ItemViewType = ItemViewType.Detail; option.Header.SortField = "SeriesSortName,SortName"; break; case HeaderMetadata.EpisodeSeries: option.Column = (i, r) => this.GetObject(i, (x) => x.SeriesName); option.Header.ItemViewType = ItemViewType.Detail; option.ItemID = (i) => { Series series = this.GetObject(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(i, (x) => x.SeriesName); option.Header.ItemViewType = ItemViewType.Detail; option.ItemID = (i) => { Season season = this.GetObject(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(i, (x) => x.GameSystem); option.Header.SortField = "GameSystem,SortName"; break; case HeaderMetadata.Players: option.Column = (i, r) => this.GetObject(i, (x) => x.PlayersSupported); option.Header.SortField = "Players,GameSystem,SortName"; break; case HeaderMetadata.AlbumArtist: option.Column = (i, r) => this.GetObject(i, (x) => x.AlbumArtist); option.ItemID = (i) => this.GetPersonID(this.GetObject(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(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>(i, (x) => x.AlbumArtists)); option.Header.SortField = "AlbumArtist,Album,SortName"; internalHeader = HeaderMetadata.AlbumArtist; break; case HeaderMetadata.AudioAlbum: option.Column = (i, r) => this.GetObject(i, (x) => x.Album); option.Header.SortField = "Album,SortName"; internalHeader = HeaderMetadata.Album; break; case HeaderMetadata.Countries: option.Column = (i, r) => this.GetListAsString(this.GetObject>(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>(i, (x) => x.Tracks.ToList(), new List