diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7086ac7437..0fbe516221 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1779,7 +1779,8 @@ namespace MediaBrowser.Controller.Entities ProviderIds = ProviderIds, IndexNumber = IndexNumber, ParentIndexNumber = ParentIndexNumber, - Year = ProductionYear + Year = ProductionYear, + PremiereDate = PremiereDate }; } diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs index 91dc33214c..7114cde3e2 100644 --- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs @@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.Providers public int? Year { get; set; } public int? IndexNumber { get; set; } public int? ParentIndexNumber { get; set; } + public DateTime? PremiereDate { get; set; } public ItemLookupInfo() { diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs index 1a6327d00f..50ecc6bbfd 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs @@ -63,97 +63,89 @@ namespace MediaBrowser.Providers.TV var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); var indexOffset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds) ?? 0; - var files = TvdbEpisodeProvider.Current.GetEpisodeXmlFiles(episode.ParentIndexNumber + indexOffset, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); + var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo()); - var result = files.Select(i => GetImageInfo(i, cancellationToken)) - .Where(i => i != null); + var result = nodes.Select(i => GetImageInfo(i, cancellationToken)) + .Where(i => i != null) + .ToList(); - return Task.FromResult(result); + return Task.FromResult>(result); } return Task.FromResult>(new RemoteImageInfo[] { }); } - private RemoteImageInfo GetImageInfo(FileSystemMetadata xmlFile, CancellationToken cancellationToken) + private RemoteImageInfo GetImageInfo(XmlReader reader, CancellationToken cancellationToken) { var height = 225; var width = 400; var url = string.Empty; - using (var streamReader = new StreamReader(xmlFile.FullName, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "thumb_width": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - width = rval; - } - } - break; - } - - case "thumb_height": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - height = rval; - } - } - break; - } - - case "filename": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - url = TVUtils.BannerUrl + val; - } - break; - } - - default: - reader.Skip(); - break; - } - } - } - } - } + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "thumb_width": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + width = rval; + } + } + break; + } + + case "thumb_height": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + height = rval; + } + } + break; + } + + case "filename": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + url = TVUtils.BannerUrl + val; + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } if (string.IsNullOrEmpty(url)) { @@ -205,11 +197,9 @@ namespace MediaBrowser.Providers.TV if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { // Process images - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); - - var files = TvdbEpisodeProvider.Current.GetEpisodeXmlFiles(episode.ParentIndexNumber, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(series.ProviderIds, series.GetPreferredMetadataLanguage()); - return files.Any(i => _fileSystem.GetLastWriteTimeUtc(i) > date); + return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > date; } } diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs index ae247c9311..9c047f45d8 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs @@ -47,12 +47,22 @@ namespace MediaBrowser.Providers.TV { var list = new List(); - if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && searchInfo.IndexNumber.HasValue) + // The search query must either provide an episode number or date + if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) + { + return list; + } + + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) { var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds); var searchNumbers = new EpisodeNumbers(); - searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + + if (searchInfo.IndexNumber.HasValue) { + searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + } + searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber; searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; @@ -97,47 +107,17 @@ namespace MediaBrowser.Providers.TV public async Task> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - var identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); - - if (identity == null) - { - await Identify(searchInfo).ConfigureAwait(false); - identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); - } - var result = new MetadataResult(); - if (identity != null) - { - var seriesProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - seriesProviderIds[MetadataProviders.Tvdb.ToString()] = identity.Value.SeriesId; - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - var searchNumbers = new EpisodeNumbers(); - searchNumbers.EpisodeNumber = identity.Value.EpisodeNumber; - var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(searchInfo.SeriesProviderIds) ?? 0; - searchNumbers.SeasonNumber = identity.Value.SeasonIndex + seasonOffset; - searchNumbers.EpisodeNumberEnd = identity.Value.EpisodeNumberEnd ?? searchNumbers.EpisodeNumber; - - try - { - result = FetchEpisodeData(searchInfo, searchNumbers, seriesDataPath, cancellationToken); - } - catch (FileNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - catch (DirectoryNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - } - else if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && searchInfo.IndexNumber.HasValue) + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && + (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) { var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds); var searchNumbers = new EpisodeNumbers(); - searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + if (searchInfo.IndexNumber.HasValue) { + searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + } searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber; searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; @@ -176,11 +156,9 @@ namespace MediaBrowser.Providers.TV if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { // Process images - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); - - var files = GetEpisodeXmlFiles(episode.ParentIndexNumber, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(series.ProviderIds, series.GetPreferredMetadataLanguage()); - return files.Any(i => _fileSystem.GetLastWriteTimeUtc(i) > date); + return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > date; } return false; @@ -194,68 +172,22 @@ namespace MediaBrowser.Providers.TV /// The ending episode number. /// The series data path. /// List{FileInfo}. - internal List GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) + internal List GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) { - var files = new List(); - - if (episodeNumber == null) - { - return files; - } - - if (seasonNumber == null) - { - return files; - } - - var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - - var fileInfo = _fileSystem.GetFileInfo(file); - var usingAbsoluteData = false; - - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - fileInfo = _fileSystem.GetFileInfo(file); - if (fileInfo.Exists) - { - files.Add(fileInfo); - usingAbsoluteData = true; - } - } - - var end = endingEpisodeNumber ?? episodeNumber; - episodeNumber++; - - while (episodeNumber <= end) - { - if (usingAbsoluteData) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - } - - fileInfo = _fileSystem.GetFileInfo(file); - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - break; - } - - episodeNumber++; - } - - return files; + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath (searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); + + try + { + return GetXmlNodes(seriesXmlPath, searchInfo); + } + catch (DirectoryNotFoundException) + { + return new List (); + } + catch (FileNotFoundException) + { + return new List (); + } } private class EpisodeNumbers @@ -275,368 +207,502 @@ namespace MediaBrowser.Providers.TV /// Task{System.Boolean}. private MetadataResult FetchEpisodeData(EpisodeInfo id, EpisodeNumbers searchNumbers, string seriesDataPath, CancellationToken cancellationToken) { - var episodeNumber = searchNumbers.EpisodeNumber; - var seasonNumber = searchNumbers.SeasonNumber; - - string file; - var usingAbsoluteData = false; + var result = new MetadataResult() + { + Item = new Episode + { + IndexNumber = id.IndexNumber, + ParentIndexNumber = id.ParentIndexNumber, + IndexNumberEnd = id.IndexNumberEnd + } + }; - var result = new MetadataResult() - { - Item = new Episode - { - IndexNumber = id.IndexNumber, - ParentIndexNumber = id.ParentIndexNumber, - IndexNumberEnd = id.IndexNumberEnd - } - }; - - try - { - if (seasonNumber != null) - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - FetchMainEpisodeInfo(result, file, cancellationToken); - - result.HasMetadata = true; - } - } - catch (FileNotFoundException) - { - // Could be using absolute numbering - if (seasonNumber.HasValue && seasonNumber.Value != 1) - { - throw; - } - } - - if (!result.HasMetadata) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); + var xmlNodes = GetEpisodeXmlNodes (seriesDataPath, id); - FetchMainEpisodeInfo(result, file, cancellationToken); - result.HasMetadata = true; - usingAbsoluteData = true; - } + if (xmlNodes.Count > 0) { + FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken); - var end = searchNumbers.EpisodeNumberEnd; - episodeNumber++; + result.HasMetadata = true; + } - while (episodeNumber <= end) - { - if (usingAbsoluteData) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - } - - try - { - FetchAdditionalPartInfo(result, file, cancellationToken); - } - catch (FileNotFoundException) - { - break; - } - catch (DirectoryNotFoundException) - { - break; - } - - episodeNumber++; - } + foreach (var node in xmlNodes.Skip(1)) { + FetchAdditionalPartInfo(result, node, cancellationToken); + } return result; } + private List GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) + { + var list = new List (); + + if (searchInfo.IndexNumber.HasValue) + { + var files = GetEpisodeXmlFiles (searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName (xmlFile)); + + list = files.Select (GetXmlReader).ToList (); + } + + if (list.Count == 0 && searchInfo.PremiereDate.HasValue) { + list = GetXmlNodesByPremiereDate (xmlFile, searchInfo.PremiereDate.Value); + } + + return list; + } + + private List GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) + { + var files = new List(); + + if (episodeNumber == null) + { + return files; + } + + if (seasonNumber == null) + { + return files; + } + + var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); + + var fileInfo = _fileSystem.GetFileInfo(file); + var usingAbsoluteData = false; + + if (fileInfo.Exists) + { + files.Add(fileInfo); + } + else + { + file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); + fileInfo = _fileSystem.GetFileInfo(file); + if (fileInfo.Exists) + { + files.Add(fileInfo); + usingAbsoluteData = true; + } + } + + var end = endingEpisodeNumber ?? episodeNumber; + episodeNumber++; + + while (episodeNumber <= end) + { + if (usingAbsoluteData) + { + file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); + } + else + { + file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); + } + + fileInfo = _fileSystem.GetFileInfo(file); + if (fileInfo.Exists) + { + files.Add(fileInfo); + } + else + { + break; + } + + episodeNumber++; + } + + return files; + } + + private XmlReader GetXmlReader(FileSystemMetadata xmlFile) + { + return GetXmlReader (_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8)); + } + + private XmlReader GetXmlReader(String xml) + { + var streamReader = new StringReader (xml); + + return XmlReader.Create (streamReader, new XmlReaderSettings { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + }); + } + + private List GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) + { + var list = new List (); + + using (var streamReader = new StreamReader (xmlFile, Encoding.UTF8)) { + // Use XmlReader for best performance + using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + })) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Episode": + { + var outerXml = reader.ReadOuterXml(); + + var airDate = GetEpisodeAirDate (outerXml); + + if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) + { + list.Add (GetXmlReader(outerXml)); + return list; + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + } + + return list; + } + + private DateTime? GetEpisodeAirDate(string xml) + { + using (var streamReader = new StringReader (xml)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + })) + { + reader.MoveToContent (); + + // Loop through each element + while (reader.Read ()) { + + if (reader.NodeType == XmlNodeType.Element) { + switch (reader.Name) { + + case "FirstAired": + { + var val = reader.ReadElementContentAsString (); + + if (!string.IsNullOrWhiteSpace (val)) { + DateTime date; + if (DateTime.TryParse (val, out date)) { + date = date.ToUniversalTime (); + + return date; + } + } + + break; + } + + default: + reader.Skip (); + break; + } + } + } + } + } + return null; + } + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private void FetchMainEpisodeInfo(MetadataResult result, string xmlFile, CancellationToken cancellationToken) + private void FetchMainEpisodeInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - result.ResetPeople(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "id": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Tvdb, val); - } - break; - } - - case "IMDB_ID": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Imdb, val); - } - break; - } - - case "DVD_episodenumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float num; - - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) - { - item.DvdEpisodeNumber = num; - } - } - - break; - } - - case "DVD_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float num; - - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) - { - item.DvdSeasonNumber = Convert.ToInt32(num); - } - } - - break; - } - - case "absolute_number": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AbsoluteEpisodeNumber = rval; - } - } - - break; - } - - case "airsbefore_episode": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsBeforeEpisodeNumber = rval; - } - } - - break; - } - - case "airsafter_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsAfterSeasonNumber = rval; - } - } - - break; - } - - case "airsbefore_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsBeforeSeasonNumber = rval; - } - } - - break; - } - - case "EpisodeName": - { - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name = val; - } - } - break; - } - - case "Overview": - { - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview = val; - } - } - break; - } - case "Rating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float rval; - - // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) - { - item.CommunityRating = rval; - } - } - break; - } - case "RatingCount": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.VoteCount = rval; - } - } - - break; - } - - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - DateTime date; - if (DateTime.TryParse(val, out date)) - { - date = date.ToUniversalTime(); - - item.PremiereDate = date; - item.ProductionYear = date.Year; - } - } - - break; - } - - case "Director": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Director); - } - } - - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } - - break; - } - case "Writer": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Writer); - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - } - } - } + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); + + result.ResetPeople(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "id": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Tvdb, val); + } + break; + } + + case "IMDB_ID": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Imdb, val); + } + break; + } + + case "DVD_episodenumber": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float num; + + if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + { + item.DvdEpisodeNumber = num; + } + } + + break; + } + + case "DVD_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float num; + + if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + { + item.DvdSeasonNumber = Convert.ToInt32(num); + } + } + + break; + } + + case "absolute_number": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AbsoluteEpisodeNumber = rval; + } + } + + break; + } + + case "airsbefore_episode": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsBeforeEpisodeNumber = rval; + } + } + + break; + } + + case "airsafter_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsAfterSeasonNumber = rval; + } + } + + break; + } + + case "airsbefore_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsBeforeSeasonNumber = rval; + } + } + + break; + } + + case "EpisodeName": + { + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Name = val; + } + } + break; + } + + case "Overview": + { + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Overview = val; + } + } + break; + } + case "Rating": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float rval; + + // float.TryParse is local aware, so it can be probamatic, force us culture + if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) + { + item.CommunityRating = rval; + } + } + break; + } + case "RatingCount": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.VoteCount = rval; + } + } + + break; + } + + case "FirstAired": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + if (DateTime.TryParse(val, out date)) + { + date = date.ToUniversalTime(); + + item.PremiereDate = date; + item.ProductionYear = date.Year; + } + } + + break; + } + + case "Director": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Director); + } + } + + break; + } + case "GuestStars": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddGuestStars(result, val); + } + } + + break; + } + case "Writer": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Writer); + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } } private void AddPeople(MetadataResult result, string val, string personType) @@ -680,108 +746,99 @@ namespace MediaBrowser.Providers.TV } } - private void FetchAdditionalPartInfo(MetadataResult result, string xmlFile, CancellationToken cancellationToken) + private void FetchAdditionalPartInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "EpisodeName": - { - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name += ", " + val; - } - } - break; - } - - case "Overview": - { - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview += Environment.NewLine + Environment.NewLine + val; - } - } - break; - } - case "Director": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Director); - } - } - - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } - - break; - } - case "Writer": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Writer); - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - } - } - } + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "EpisodeName": + { + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Name += ", " + val; + } + } + break; + } + + case "Overview": + { + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Overview += Environment.NewLine + Environment.NewLine + val; + } + } + break; + } + case "Director": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Director); + } + } + + break; + } + case "GuestStars": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddGuestStars(result, val); + } + } + + break; + } + case "Writer": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Writer); + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index d63022900b..7250dbee4c 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -161,9 +161,7 @@ namespace MediaBrowser.Providers.TV var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - var seriesXmlFilename = metadataLanguage.ToLower() + ".xml"; - - var seriesXmlPath = Path.Combine(seriesDataPath, seriesXmlFilename); + var seriesXmlPath = GetSeriesXmlPath (seriesProviderIds, metadataLanguage); var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml"); FetchSeriesInfo(series, seriesXmlPath, cancellationToken); @@ -1278,6 +1276,15 @@ namespace MediaBrowser.Providers.TV return null; } + public string GetSeriesXmlPath(Dictionary seriesProviderIds, string language) + { + var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); + + var seriesXmlFilename = language.ToLower() + ".xml"; + + return Path.Combine (seriesDataPath, seriesXmlFilename); + } + /// /// Gets the series data path. /// diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj index 72310f5e96..2dd111ea7e 100644 --- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj +++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj @@ -1067,9 +1067,6 @@ Resources\dashboard-ui\bower_components\emby-apiclient\credentials.js - - Resources\dashboard-ui\bower_components\emby-apiclient\deferred.js - Resources\dashboard-ui\bower_components\emby-apiclient\events.js @@ -1079,9 +1076,6 @@ Resources\dashboard-ui\bower_components\emby-apiclient\localassetmanager.js - - Resources\dashboard-ui\bower_components\emby-apiclient\logger.js - Resources\dashboard-ui\bower_components\emby-apiclient\serverdiscovery.js @@ -1109,6 +1103,21 @@ Resources\dashboard-ui\bower_components\emby-icons\emby-icons.html + + Resources\dashboard-ui\bower_components\emby-webcomponents\.bower.json + + + Resources\dashboard-ui\bower_components\emby-webcomponents\LICENSE + + + Resources\dashboard-ui\bower_components\emby-webcomponents\README.md + + + Resources\dashboard-ui\bower_components\emby-webcomponents\bower.json + + + Resources\dashboard-ui\bower_components\emby-webcomponents\requirehtml.js + Resources\dashboard-ui\bower_components\fastclick\.bower.json @@ -4175,9 +4184,6 @@ Resources\dashboard-ui\components\requirecss.js - - Resources\dashboard-ui\components\requirehtml.js - Resources\dashboard-ui\components\sharingwidget.js @@ -4667,6 +4673,9 @@ Resources\dashboard-ui\legacy\buttonenabled.js + + Resources\dashboard-ui\legacy\deferred.js + Resources\dashboard-ui\scripts\aboutpage.js