aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api/Reports/Data
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api/Reports/Data')
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportBuilder.cs589
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportExport.cs212
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportGroup.cs44
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportHeader.cs54
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportItem.cs34
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportOptions.cs52
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportResult.cs53
-rw-r--r--MediaBrowser.Api/Reports/Data/ReportRow.cs71
8 files changed, 1109 insertions, 0 deletions
diff --git a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
new file mode 100644
index 000000000..00ce18317
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs
@@ -0,0 +1,589 @@
+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
+{
+ /// <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>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.Series,
+ HeaderMetadata.Season,
+ HeaderMetadata.SeasonNumber,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres
+ };
+
+ case ReportViewType.Series:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.Name,
+ HeaderMetadata.Network,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres,
+ HeaderMetadata.ParentalRating,
+ HeaderMetadata.CommunityRating,
+ HeaderMetadata.Runtime,
+ HeaderMetadata.Trailers,
+ HeaderMetadata.Specials
+ };
+
+ case ReportViewType.MusicAlbum:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.Name,
+ HeaderMetadata.AlbumArtist,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.ReleaseDate,
+ HeaderMetadata.Tracks,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres
+ };
+
+ case ReportViewType.MusicArtist:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.MusicArtist,
+ HeaderMetadata.Countries,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres
+ };
+
+ case ReportViewType.Game:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.Name,
+ HeaderMetadata.GameSystem,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.ReleaseDate,
+ HeaderMetadata.ParentalRating,
+ HeaderMetadata.CommunityRating,
+ HeaderMetadata.Players,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres,
+ HeaderMetadata.Trailers
+ };
+
+ case ReportViewType.Movie:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ 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 ReportViewType.Book:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.Name,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.ReleaseDate,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres,
+ HeaderMetadata.ParentalRating,
+ HeaderMetadata.CommunityRating
+ };
+
+ case ReportViewType.BoxSet:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ HeaderMetadata.Name,
+ HeaderMetadata.DateAdded,
+ HeaderMetadata.ReleaseDate,
+ HeaderMetadata.Year,
+ HeaderMetadata.Genres,
+ HeaderMetadata.ParentalRating,
+ HeaderMetadata.CommunityRating,
+ HeaderMetadata.Trailers
+ };
+
+ case ReportViewType.Audio:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ 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 ReportViewType.Episode:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ 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 ReportViewType.Video:
+ case ReportViewType.MusicVideo:
+ case ReportViewType.Trailer:
+ case ReportViewType.BaseItem:
+ default:
+ return new List<HeaderMetadata>
+ {
+ HeaderMetadata.StatusImage,
+ 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
+ };
+
+ }
+
+ }
+
+ /// <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;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportExport.cs b/MediaBrowser.Api/Reports/Data/ReportExport.cs
new file mode 100644
index 000000000..f313cf252
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportExport.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+ /// <summary> A report export. </summary>
+ public class ReportExport
+ {
+ /// <summary> Export to CSV. </summary>
+ /// <param name="reportResult"> The report result. </param>
+ /// <returns> A string. </returns>
+ public string ExportToCsv(ReportResult reportResult)
+ {
+ StringBuilder returnValue = new StringBuilder();
+
+ returnValue.AppendLine(string.Join(";", reportResult.Headers.Select(s => s.Name.Replace(',', ' ')).ToArray()));
+
+ if (reportResult.IsGrouped)
+ foreach (ReportGroup group in reportResult.Groups)
+ {
+ foreach (ReportRow row in reportResult.Rows)
+ {
+ returnValue.AppendLine(string.Join(";", row.Columns.Select(s => s.Name.Replace(',', ' ')).ToArray()));
+ }
+ }
+ else
+ foreach (ReportRow row in reportResult.Rows)
+ {
+ returnValue.AppendLine(string.Join(";", row.Columns.Select(s => s.Name.Replace(',', ' ')).ToArray()));
+ }
+
+ return returnValue.ToString();
+ }
+
+
+ /// <summary> Export to excel. </summary>
+ /// <param name="reportResult"> The report result. </param>
+ /// <returns> A string. </returns>
+ public string ExportToExcel(ReportResult reportResult)
+ {
+
+ string style = @"<style type='text/css'>
+ BODY {
+ font-family: Arial;
+ font-size: 12px;
+ }
+
+ TABLE {
+ font-family: Arial;
+ font-size: 12px;
+ }
+
+ A {
+ font-family: Arial;
+ color: #144A86;
+ font-size: 12px;
+ cursor: pointer;
+ text-decoration: none;
+ font-weight: bold;
+ }
+ DIV {
+ font-family: Arial;
+ font-size: 12px;
+ margin-bottom: 0px;
+ }
+ P, LI, DIV {
+ font-size: 12px;
+ margin-bottom: 0px;
+ }
+
+ P, UL {
+ font-size: 12px;
+ margin-bottom: 6px;
+ margin-top: 0px;
+ }
+
+ H1 {
+ font-size: 18pt;
+ }
+
+ H2 {
+ font-weight: bold;
+ font-size: 14pt;
+ COLOR: #C0C0C0;
+ }
+
+ H3 {
+ font-weight: normal;
+ font-size: 14pt;
+ text-indent: +1em;
+ }
+
+ H4 {
+ font-size: 10pt;
+ font-weight: normal;
+ }
+
+ H5 {
+ font-size: 10pt;
+ font-weight: normal;
+ background: #A9A9A9;
+ COLOR: white;
+ display: inline;
+ }
+
+ H6 {
+ padding: 2 1 2 5;
+ font-size: 11px;
+ font-weight: bold;
+ text-decoration: none;
+ margin-bottom: 1px;
+ }
+
+ UL {
+ line-height: 1.5em;
+ list-style-type: disc;
+ }
+
+ OL {
+ line-height: 1.5em;
+ }
+
+ LI {
+ line-height: 1.5em;
+ }
+
+ A IMG {
+ border: 0;
+ }
+
+ table.gridtable {
+ color: #333333;
+ border-width: 0.1pt;
+ border-color: #666666;
+ border-collapse: collapse;
+ }
+
+ table.gridtable th {
+ border-width: 0.1pt;
+ padding: 8px;
+ border-style: solid;
+ border-color: #666666;
+ background-color: #dedede;
+ }
+ table.gridtable tr {
+ background-color: #ffffff;
+ }
+ table.gridtable td {
+ border-width: 0.1pt;
+ padding: 8px;
+ border-style: solid;
+ border-color: #666666;
+ background-color: #ffffff;
+ }
+ </style>";
+
+ string Html = @"<!DOCTYPE html>
+ <html xmlns='http://www.w3.org/1999/xhtml'>
+ <head>
+ <meta http-equiv='X-UA-Compatible' content='IE=8, IE=9, IE=10' />
+ <meta charset='utf-8'>
+ <title>Emby Reports Export</title>";
+ Html += "\n" + style + "\n";
+ Html += "</head>\n";
+ Html += "<body>\n";
+
+ StringBuilder returnValue = new StringBuilder();
+ returnValue.AppendLine("<table class='gridtable'>");
+ returnValue.AppendLine("<tr>");
+ returnValue.AppendLine(string.Join("", reportResult.Headers.Select(s => string.Format("<th>{0}</th>", s.Name)).ToArray()));
+ returnValue.AppendLine("</tr>");
+ if (reportResult.IsGrouped)
+ foreach (ReportGroup group in reportResult.Groups)
+ {
+ returnValue.AppendLine("<tr>");
+ returnValue.AppendLine("<th scope='rowgroup' colspan='" + reportResult.Headers.Count + "'>" + (string.IsNullOrEmpty(group.Name) ? "&nbsp;" : group.Name) + "</th>");
+ returnValue.AppendLine("</tr>");
+ foreach (ReportRow row in group.Rows)
+ {
+ ExportToExcelRow(reportResult, returnValue, row);
+ }
+ returnValue.AppendLine("<tr>");
+ returnValue.AppendLine("<th style='background-color: #ffffff;' scope='rowgroup' colspan='" + reportResult.Headers.Count + "'>" + "&nbsp;" + "</th>");
+ returnValue.AppendLine("</tr>");
+ }
+
+ else
+ foreach (ReportRow row in reportResult.Rows)
+ {
+ ExportToExcelRow(reportResult, returnValue, row);
+ }
+ returnValue.AppendLine("</table>");
+
+ Html += returnValue.ToString();
+ Html += "</body>";
+ Html += "</html>";
+ return Html;
+ }
+ private static void ExportToExcelRow(ReportResult reportResult,
+ StringBuilder returnValue,
+ ReportRow row)
+ {
+ returnValue.AppendLine("<tr>");
+ returnValue.AppendLine(string.Join("", row.Columns.Select(s => string.Format("<td>{0}</td>", s.Name)).ToArray()));
+ returnValue.AppendLine("</tr>");
+ }
+ }
+
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportGroup.cs b/MediaBrowser.Api/Reports/Data/ReportGroup.cs
new file mode 100644
index 000000000..49c76c7ba
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportGroup.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+
+ /// <summary> A report group. </summary>
+ public class ReportGroup
+ {
+ /// <summary>
+ /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportGroup class. </summary>
+ public ReportGroup()
+ {
+ Rows = new List<ReportRow>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportGroup class. </summary>
+ /// <param name="rows"> The rows. </param>
+ public ReportGroup(List<ReportRow> rows)
+ {
+ Rows = rows;
+ }
+
+ /// <summary> Gets or sets the name. </summary>
+ /// <value> The name. </value>
+ public string Name { get; set; }
+
+ /// <summary> Gets or sets the rows. </summary>
+ /// <value> The rows. </value>
+ public List<ReportRow> Rows { get; set; }
+
+ /// <summary> Returns a string that represents the current object. </summary>
+ /// <returns> A string that represents the current object. </returns>
+ /// <seealso cref="M:System.Object.ToString()"/>
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportHeader.cs b/MediaBrowser.Api/Reports/Data/ReportHeader.cs
new file mode 100644
index 000000000..81b85954a
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportHeader.cs
@@ -0,0 +1,54 @@
+using MediaBrowser.Controller.Entities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+ /// <summary> A report header. </summary>
+ public class ReportHeader
+ {
+ /// <summary> Initializes a new instance of the ReportHeader class. </summary>
+ public ReportHeader()
+ {
+ ItemViewType = ItemViewType.None;
+ Visible = true;
+ CanGroup = true;
+ }
+
+ /// <summary> Gets or sets the type of the header field. </summary>
+ /// <value> The type of the header field. </value>
+ public ReportFieldType HeaderFieldType { get; set; }
+
+ /// <summary> Gets or sets the name of the header. </summary>
+ /// <value> The name of the header. </value>
+ public string Name { get; set; }
+
+ /// <summary> Gets or sets the name of the field. </summary>
+ /// <value> The name of the field. </value>
+ public HeaderMetadata FieldName { get; set; }
+
+ /// <summary> Gets or sets the sort field. </summary>
+ /// <value> The sort field. </value>
+ public string SortField { get; set; }
+
+ /// <summary> Gets or sets the type. </summary>
+ /// <value> The type. </value>
+ public string Type { get; set; }
+
+ /// <summary> Gets or sets the type of the item view. </summary>
+ /// <value> The type of the item view. </value>
+ public ItemViewType ItemViewType { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether this object is visible. </summary>
+ /// <value> true if visible, false if not. </value>
+ public bool Visible { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether we can group. </summary>
+ /// <value> true if we can group, false if not. </value>
+ public bool CanGroup { get; set; }
+
+ }
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportItem.cs b/MediaBrowser.Api/Reports/Data/ReportItem.cs
new file mode 100644
index 000000000..06d0b0c46
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportItem.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+ /// <summary> A report item. </summary>
+ public class ReportItem
+ {
+ /// <summary> Gets or sets the identifier. </summary>
+ /// <value> The identifier. </value>
+ public string Id { get; set; }
+
+ /// <summary> Gets or sets the name. </summary>
+ /// <value> The name. </value>
+ public string Name { get; set; }
+
+ public string Image { get; set; }
+
+ /// <summary> Gets or sets the custom tag. </summary>
+ /// <value> The custom tag. </value>
+ public string CustomTag { get; set; }
+
+ /// <summary> Returns a string that represents the current object. </summary>
+ /// <returns> A string that represents the current object. </returns>
+ /// <seealso cref="M:System.Object.ToString()"/>
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportOptions.cs b/MediaBrowser.Api/Reports/Data/ReportOptions.cs
new file mode 100644
index 000000000..aed15d428
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportOptions.cs
@@ -0,0 +1,52 @@
+using MediaBrowser.Controller.Entities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+ /// <summary> A report options. </summary>
+ internal class ReportOptions<I>
+ {
+ /// <summary> Initializes a new instance of the ReportOptions class. </summary>
+ public ReportOptions()
+ {
+ }
+
+ /// <summary> Initializes a new instance of the ReportOptions class. </summary>
+ /// <param name="header"> . </param>
+ /// <param name="row"> . </param>
+ public ReportOptions(ReportHeader header, Func<I, ReportRow, object> column)
+ {
+ Header = header;
+ Column = column;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the ReportOptions class.
+ /// </summary>
+ /// <param name="header"></param>
+ /// <param name="column"></param>
+ /// <param name="itemID"></param>
+ public ReportOptions(ReportHeader header, Func<I, ReportRow, object> column, Func<I, object> itemID)
+ {
+ Header = header;
+ Column = column;
+ ItemID = itemID;
+ }
+
+ /// <summary> Gets or sets the header. </summary>
+ /// <value> The header. </value>
+ public ReportHeader Header { get; set; }
+
+ /// <summary> Gets or sets the column. </summary>
+ /// <value> The column. </value>
+ public Func<I, ReportRow, object> Column { get; set; }
+
+ /// <summary> Gets or sets the identifier of the item. </summary>
+ /// <value> The identifier of the item. </value>
+ public Func<I, object> ItemID { get; set; }
+ }
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportResult.cs b/MediaBrowser.Api/Reports/Data/ReportResult.cs
new file mode 100644
index 000000000..a4bc95aa1
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportResult.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Api.Reports
+{
+
+ /// <summary> Encapsulates the result of a report. </summary>
+ public class ReportResult
+ {
+ /// <summary>
+ /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportResult class. </summary>
+ public ReportResult()
+ {
+ Rows = new List<ReportRow>();
+ Headers = new List<ReportHeader>();
+ Groups = new List<ReportGroup>();
+ TotalRecordCount = 0;
+ IsGrouped = false;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportResult class. </summary>
+ /// <param name="headers"> The headers. </param>
+ /// <param name="rows"> The rows. </param>
+ public ReportResult(List<ReportHeader> headers, List<ReportRow> rows)
+ {
+ Rows = rows;
+ Headers = headers;
+ TotalRecordCount = 0;
+ }
+
+ /// <summary> Gets or sets the rows. </summary>
+ /// <value> The rows. </value>
+ public List<ReportRow> Rows { get; set; }
+
+ /// <summary> Gets or sets the headers. </summary>
+ /// <value> The headers. </value>
+ public List<ReportHeader> Headers { get; set; }
+
+ /// <summary> Gets or sets the groups. </summary>
+ /// <value> The groups. </value>
+ public List<ReportGroup> Groups { get; set; }
+
+
+ /// <summary> Gets or sets the number of total records. </summary>
+ /// <value> The total number of record count. </value>
+ public int TotalRecordCount { get; set; }
+
+ /// <summary> Gets or sets the is grouped. </summary>
+ /// <value> The is grouped. </value>
+ public bool IsGrouped { get; set; }
+
+ }
+}
diff --git a/MediaBrowser.Api/Reports/Data/ReportRow.cs b/MediaBrowser.Api/Reports/Data/ReportRow.cs
new file mode 100644
index 000000000..f2165344a
--- /dev/null
+++ b/MediaBrowser.Api/Reports/Data/ReportRow.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+ public class ReportRow
+ {
+ /// <summary>
+ /// Initializes a new instance of the ReportRow class.
+ /// </summary>
+ public ReportRow()
+ {
+ Columns = new List<ReportItem>();
+ }
+
+ /// <summary> Gets or sets the identifier. </summary>
+ /// <value> The identifier. </value>
+ public string Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this object has backdrop image. </summary>
+ /// <value> true if this object has backdrop image, false if not. </value>
+ public bool HasImageTagsBackdrop { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether this object has image tags. </summary>
+ /// <value> true if this object has image tags, false if not. </value>
+ public bool HasImageTagsPrimary { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this object has image tags logo. </summary>
+ /// <value> true if this object has image tags logo, false if not. </value>
+ public bool HasImageTagsLogo { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this object has local trailer. </summary>
+ /// <value> true if this object has local trailer, false if not. </value>
+ public bool HasLocalTrailer { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether this object has lock data. </summary>
+ /// <value> true if this object has lock data, false if not. </value>
+ public bool HasLockData { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this object has embedded image. </summary>
+ /// <value> true if this object has embedded image, false if not. </value>
+ public bool HasEmbeddedImage { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether this object has subtitles. </summary>
+ /// <value> true if this object has subtitles, false if not. </value>
+ public bool HasSubtitles { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether this object has specials. </summary>
+ /// <value> true if this object has specials, false if not. </value>
+ public bool HasSpecials { get; set; }
+
+ /// <summary> Gets or sets a value indicating whether this object is unidentified. </summary>
+ /// <value> true if this object is unidentified, false if not. </value>
+ public bool IsUnidentified { get; set; }
+
+ /// <summary> Gets or sets the columns. </summary>
+ /// <value> The columns. </value>
+ public List<ReportItem> Columns { get; set; }
+
+ /// <summary> Gets or sets the type. </summary>
+ /// <value> The type. </value>
+ public ReportViewType RowType { get; set; }
+ }
+}