public Task<MetadataRawTable> GetMetadataRaw(IHasMetadata item, string language, CancellationToken cancellationToken)
        {
            var table = new MetadataRawTable();

            var service = _metadataServices.FirstOrDefault(i => i.CanRefresh(item));

            if (service == null)
            {
                _logger.Error("Unable to find a metadata service for item of type " + item.GetType().Name);
                return null;
            }

            return this.GetMetadataRaw(item, service, language, cancellationToken);
        }
        public async Task<MetadataRawTable> GetMetadataRaw(IHasMetadata item, IMetadataService service, string language, CancellationToken cancellationToken)
        {
            List<string> ignoreProperties = new List<string>(new[] { "LocalAlternateVersions", "LinkedAlternateVersions", "IsThemeMedia", 
                "SupportsAddingToPlaylist", "AlwaysScanInternalMetadataPath", "IsFolder", "IsTopParent", "SupportsAncestors",
                "ParentId", "Parents", "PhysicalLocations", "LockedFields", "IsLocked", "DisplayPreferencesId", "Id", "ImageInfos", "SubtitleFiles", 
                "HasSubtitles", "IsPlaceHolder", "IsShortcut", "SupportsRemoteImageDownloading", "AdditionalParts", "IsStacked", "HasLocalAlternateVersions", 
                "IsArchive", "IsOffline", "IsHidden", "IsOwnedItem", "MediaSourceCount", "VideoType", "PlayableStreamFileNames", "Is3D", 
                "IsInMixedFolder", "SupportsLocalMetadata", "IndexByOptionStrings", "IsInSeasonFolder", "IsMissingEpisode", "IsVirtualUnaired",
                "ContainsEpisodesWithoutSeasonFolders", "IsPhysicalRoot", "IsPreSorted", "DisplaySpecialsWithSeasons", "IsRoot", "IsVirtualFolder", 
                "LinkedChildren", "Children", "RecursiveChildren", "LocationType", "SpecialFeatureIds", "LocalTrailerIds", "RemoteTrailerIds", 
                "RemoteTrailers", "ThemeSongIds", "ThemeVideoIds", "PresentationUniqueKey", "EnableRememberingTrackSelections", "EnableAlphaNumericSorting",
                "SupportsCumulativeRunTimeTicks", "SupportsUserDataFromChildren", "DisplayParentId", "SupportsDateLastMediaAdded" });

            var itemOfType = item as BaseItem;
            var lookupItem = item as IHasLookupInfo<ItemLookupInfo>;
            if (itemOfType == null || lookupItem == null)
            {
                _logger.Warn("GetMetadataRaw cannot run for {0}", item);
                return null;
            }

            var serviceType = service.GetType();
            var serviceTypeInfo = service.GetType().GetTypeInfo();
            var serviceGenericTypes = serviceTypeInfo.BaseType.GenericTypeArguments;
            var serviceItemType = serviceGenericTypes[0];
            var serviceIdType = serviceGenericTypes[1];

            var table = new MetadataRawTable();
            var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
            var resultItems = new Dictionary<string, MetadataResultProxy>();
            var emptyItem = new MetadataResultProxy(CreateNew(item.GetType()), null);
            var lookupInfo = lookupItem.GetLookupInfo();

            if (!string.IsNullOrWhiteSpace(language))
            {
                lookupInfo.MetadataLanguage = language;
            }

            var providers = GetProviders(item).ToList();
            ////var remoteProviders = new List<IRemoteMetadataProvider<BaseItem, ItemLookupInfo>>();

            foreach (var providerCandidate in providers)
            {
                foreach (var ifType in providerCandidate.GetType().GetInterfaces())
                {
                    var providerGenericTypes = ifType.GenericTypeArguments;
                    if (providerGenericTypes.Length == 2)
                    {
                        if (providerGenericTypes[0].Equals(serviceItemType) && providerGenericTypes[1].Equals(serviceIdType))
                        {
                            var providerName = providerCandidate.GetType().Name;
                            _logger.Debug("Running {0} for {1}", providerName, logName);

                            table.Headers.Add(providerName);

                            try
                            {
                                var mi = ifType.GetMethod("GetMetadata");
                                dynamic task = mi.Invoke(providerCandidate, new object[] { lookupInfo, cancellationToken });
                                await task;

                                var result = task.Result;

                                if (result.HasMetadata)
                                {
                                    resultItems.Add(providerName, new MetadataResultProxy(result.Item, result.People));
                                }
                                else
                                {
                                    _logger.Debug("{0} returned no metadata for {1}", providerName, logName);
                                }
                            }
                            catch (OperationCanceledException)
                            {
                                throw;
                            }
                            catch (Exception ex)
                            {
                                _logger.ErrorException("Error in {0}", ex, providerCandidate.Name);
                            }

                            if (!resultItems.ContainsKey(providerName))
                            {
                                resultItems.Add(providerName, emptyItem);
                            }
                        }
                    }
                }
            }

            FillLookupData(lookupInfo, item, table);

            var propInfos = GetItemProperties(item.GetType());
            bool hasPeople = false;

            foreach (var propInfo in propInfos)
            {
                bool addRow = false;
                var emptyValue = propInfo.GetValue(emptyItem.Item);

                var row = new MetadataRawTable.MetadataRawRow();
                row.Caption = propInfo.Name;

                foreach (var key in resultItems.Keys)
                {
                    MetadataResultProxy resultItem = resultItems[key];

                    if (resultItem.Equals(emptyItem))
                    {
                        row.Values.Add(null);
                    }
                    else
                    {
                        if (resultItem.Persons != null && resultItem.Persons.Count > 0)
                        {
                            hasPeople = true;
                        }

                        var value = propInfo.GetValue(resultItem.Item);

                        if (propInfo.PropertyType == typeof(DateTime))
                        {
                            DateTime dateValue = (DateTime)value;

                            row.Values.Add(dateValue.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern));
                            if (dateValue != (DateTime)emptyValue)
                            {
                                addRow = true;
                            }
                        }
                        else if (propInfo.PropertyType == typeof(DateTime?))
                        {
                            DateTime? dateValue = (DateTime?)value;

                            if (dateValue.HasValue)
                            {
                                row.Values.Add(dateValue.Value.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern));
                                if (dateValue != (DateTime?)emptyValue)
                                {
                                    addRow = true;
                                }
                            }
                            else
                            {
                                row.Values.Add(null);
                            }
                        }
                        else if (propInfo.Name == "ProviderIds")
                        {
                            var providerIds = value as Dictionary<string, string>;

                            if (providerIds != null && providerIds.Count > 0)
                            {
                                var ids = FlattenProviderIds(providerIds, item, "<br />");
                                row.Values.Add(ids);
                                addRow = true;
                            }
                            else
                            {
                                row.Values.Add(null);
                            }
                        }
                        else
                        {
                            row.Values.Add(value);
                            if (value != emptyValue)
                            {
                                addRow = true;
                            }
                        }
                    }
                }

                if (addRow && !ignoreProperties.Contains(row.Caption))
                {
                    table.Rows.Add(row);
                }
            }

            if (hasPeople)
            {
                var row = new MetadataRawTable.MetadataRawRow();
                row.Caption = "People";

                foreach (var key in resultItems.Keys)
                {
                    MetadataResultProxy resultItem = resultItems[key];

                    if (resultItem.Persons == null || resultItem.Persons.Count == 0)
                    {
                        row.Values.Add(null);
                    }
                    else
                    {
                        var peopleString = "";

                        foreach (PersonInfo person in resultItem.Persons)
                        {
                            peopleString += string.Format("{0}: {1} ({2})<br />", person.Type, person.Name, person.Role);
                        }

                        row.Values.Add(peopleString);
                    }
                }

                table.Rows.Add(row);
            }

            return table;
        }
        private void FillLookupData(ItemLookupInfo lookupInfo, IHasMetadata item, MetadataRawTable table)
        {
            var propInfos = GetItemProperties(lookupInfo.GetType());

            var name = lookupInfo.Name;

            if (lookupInfo.Year.HasValue)
            {
                name = string.Format("{0} ({1})", name, lookupInfo.Year.Value);
            }

            table.LookupData.Add(new KeyValuePair<string, object>("Name", name));

            foreach (var propInfo in propInfos)
            {
                switch (propInfo.Name)
                {
                    case "ProviderIds":
                        table.LookupData.Add(new KeyValuePair<string, object>(propInfo.Name, FlattenProviderIds(lookupInfo.ProviderIds, item, ", ")));
                        break;
                    case "SeriesProviderIds":
                        var seasonInfo = lookupInfo as SeasonInfo;
                        if (seasonInfo != null)
                        {
                            table.LookupData.Add(new KeyValuePair<string, object>(propInfo.Name, FlattenProviderIds(seasonInfo.SeriesProviderIds, new Series(), ", ")));
                        }

                        var episodeInfo = lookupInfo as EpisodeInfo;
                        if (episodeInfo != null)
                        {
                            table.LookupData.Add(new KeyValuePair<string, object>(propInfo.Name, FlattenProviderIds(episodeInfo.SeriesProviderIds, new Series(), ", ")));
                        }

                        break;
                    case "Name":
                    case "Year":
                    case "IsAutomated":
                    case "MetadataCountryCode":
                        break;
                    default:
                        var value = propInfo.GetValue(lookupInfo);
                        if (value != null)
                        {
                            if (propInfo.PropertyType == typeof(DateTime?))
                            {
                                DateTime? dateValue = (DateTime?)value;
                                table.LookupData.Add(new KeyValuePair<string, object>(propInfo.Name, dateValue.Value.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern)));
                            }
                            else
                            {
                                table.LookupData.Add(new KeyValuePair<string, object>(propInfo.Name, value));
                            }
                        }

                        break;
                }
            }
        }