private async Task HandleSeries(Series series) { var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb)); if (tvdbId == 0) { return; } var children = series.GetRecursiveChildren(); var existingSeasons = new List <Season>(); var existingEpisodes = new Dictionary <int, List <Episode> >(); for (var i = 0; i < children.Count; i++) { switch (children[i]) { case Season season: if (season.IndexNumber.HasValue) { existingSeasons.Add(season); } break; case Episode episode: var seasonNumber = episode.ParentIndexNumber ?? 1; if (!existingEpisodes.ContainsKey(seasonNumber)) { existingEpisodes[seasonNumber] = new List <Episode>(); } existingEpisodes[seasonNumber].Add(episode); break; } } var allEpisodes = await GetAllEpisodes(tvdbId, series.GetPreferredMetadataLanguage()).ConfigureAwait(false); var allSeasons = allEpisodes .Where(ep => ep.AiredSeason.HasValue) .Select(ep => ep.AiredSeason.Value) .Distinct() .ToList(); // Add missing seasons var newSeasons = AddMissingSeasons(series, existingSeasons, allSeasons); AddMissingEpisodes(existingEpisodes, allEpisodes, existingSeasons.Concat(newSeasons).ToList()); }
private RemoteImageInfo GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken) { var tvdbPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb)); var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); try { return GetImageInfo(actorXmlPath, personName, cancellationToken); } catch (FileNotFoundException) { return null; } }
/// <summary> /// Downloads the image from series. /// </summary> /// <param name="item">The item.</param> /// <param name="series">The series.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> private async Task DownloadImageFromSeries(BaseItem item, Series series, CancellationToken cancellationToken) { var tvdbPath = RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb)); var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); var xmlDoc = new XmlDocument(); xmlDoc.Load(actorXmlPath); var actorNodes = xmlDoc.SelectNodes("//Actor"); if (actorNodes == null) { return; } foreach (var actorNode in actorNodes.OfType<XmlNode>()) { var name = actorNode.SafeGetString("Name"); if (string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase)) { var image = actorNode.SafeGetString("Image"); if (!string.IsNullOrEmpty(image)) { var url = TVUtils.BannerUrl + image; await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); } break; } } }
/// <summary> /// Add or remove a Show(Series) to/from the users trakt.tv library /// </summary> /// <param name="show">The show to remove</param> /// <param name="traktUser">The user who's library is being updated</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="eventType"></param> /// <returns>Task{TraktResponseDataContract}.</returns> public async Task<TraktSyncResponse> SendLibraryUpdateAsync(Series show, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType) { if (show == null) throw new ArgumentNullException("show"); if (traktUser == null) throw new ArgumentNullException("traktUser"); if (eventType == EventType.Update) return null; var showPayload = new List<TraktShowCollected> { new TraktShowCollected { Title = show.Name, Year = show.ProductionYear, Ids = new TraktShowId { Tvdb = show.GetProviderId(MetadataProviders.Tvdb).ConvertToInt(), Imdb = show.GetProviderId(MetadataProviders.Imdb), TvRage = show.GetProviderId(MetadataProviders.TvRage).ConvertToInt() }, } }; var data = new TraktSyncCollected { Shows = showPayload.ToList() }; var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove; var response = await PostToTrakt(url, data, cancellationToken, traktUser); return _jsonSerializer.DeserializeFromStream<TraktSyncResponse>(response); }
private void FetchDataFromSeriesNode(Series item, XmlReader reader, CancellationToken cancellationToken) { reader.MoveToContent(); // Loop through each element while (reader.Read()) { cancellationToken.ThrowIfCancellationRequested(); if (reader.NodeType == XmlNodeType.Element) { switch (reader.Name) { case "SeriesName": { if (!item.LockedFields.Contains(MetadataFields.Name)) { item.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); } break; } case "Overview": { if (!item.LockedFields.Contains(MetadataFields.Overview)) { item.Overview = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); } break; } case "Airs_DayOfWeek": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { item.AirDays = TVUtils.GetAirDays(val); } break; } case "Airs_Time": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { item.AirTime = val; } break; } case "ContentRating": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { if (!item.LockedFields.Contains(MetadataFields.OfficialRating)) { item.OfficialRating = val; } } break; } case "Rating": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { // Only fill this if it doesn't already have a value, since we get it from imdb which has better data if (!item.CommunityRating.HasValue || string.IsNullOrWhiteSpace(item.GetProviderId(MetadataProviders.Imdb))) { 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 "IMDB_ID": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { item.SetProviderId(MetadataProviders.Imdb, val); } break; } case "zap2it_id": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { item.SetProviderId(MetadataProviders.Zap2It, val); } break; } case "Status": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { SeriesStatus seriesStatus; if (Enum.TryParse(val, true, out seriesStatus)) item.Status = seriesStatus; } 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 "Runtime": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val) && !item.LockedFields.Contains(MetadataFields.Runtime)) { 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.RunTimeTicks = TimeSpan.FromMinutes(rval).Ticks; } } break; } case "Genre": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { // Only fill this in if there's no existing genres, because Imdb data from Omdb is preferred if (!item.LockedFields.Contains(MetadataFields.Genres) && (item.Genres.Count == 0 || !string.Equals(item.GetPreferredMetadataLanguage(), "en", StringComparison.OrdinalIgnoreCase))) { var vals = val .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()) .Where(i => !string.IsNullOrWhiteSpace(i)) .ToList(); if (vals.Count > 0) { item.Genres.Clear(); foreach (var genre in vals) { item.AddGenre(genre); } } } } break; } case "Network": { var val = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(val)) { if (!item.LockedFields.Contains(MetadataFields.Studios)) { var vals = val .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()) .Where(i => !string.IsNullOrWhiteSpace(i)) .ToList(); if (vals.Count > 0) { item.Studios.Clear(); foreach (var genre in vals) { item.AddStudio(genre); } } } } break; } default: reader.Skip(); break; } } } }
private void AttachSoundtrackIds(BaseItemDto dto, Series item, User user) { var tvdb = item.GetProviderId(MetadataProviders.Tvdb); if (string.IsNullOrEmpty(tvdb)) { return; } var recursiveChildren = user == null ? _libraryManager.RootFolder.RecursiveChildren : user.RootFolder.GetRecursiveChildren(user); dto.SoundtrackIds = recursiveChildren .Where(i => { if (!string.IsNullOrEmpty(tvdb) && string.Equals(tvdb, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase) && i is MusicAlbum) { return true; } return false; }) .Select(GetClientItemId) .ToArray(); }
public static bool IsMatch(Series item, TraktShow show) { var tvdb = item.GetProviderId(MetadataProviders.Tvdb); if (!string.IsNullOrWhiteSpace(tvdb) && string.Equals(tvdb, show.Ids.Tvdb.ToString(), StringComparison.OrdinalIgnoreCase)) { return true; } var imdb = item.GetProviderId(MetadataProviders.Imdb); if (!string.IsNullOrWhiteSpace(imdb) && string.Equals(imdb, show.Ids.Imdb, StringComparison.OrdinalIgnoreCase)) { return true; } return false; }
public async Task Run(Series series, CancellationToken cancellationToken) { var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); // Can't proceed without a tvdb id if (string.IsNullOrEmpty(tvdbId)) { return; } var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, tvdbId); var episodeFiles = Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.TopDirectoryOnly) .Select(Path.GetFileNameWithoutExtension) .Where(i => i.StartsWith("episode-", StringComparison.OrdinalIgnoreCase)) .ToList(); var episodeLookup = episodeFiles .Select(i => { var parts = i.Split('-'); if (parts.Length == 3) { int seasonNumber; if (int.TryParse(parts[1], NumberStyles.Integer, UsCulture, out seasonNumber)) { int episodeNumber; if (int.TryParse(parts[2], NumberStyles.Integer, UsCulture, out episodeNumber)) { return new Tuple<int, int>(seasonNumber, episodeNumber); } } } return new Tuple<int, int>(-1, -1); }) .Where(i => i.Item1 != -1 && i.Item2 != -1) .ToList(); var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(series, episodeLookup, cancellationToken) .ConfigureAwait(false); var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(series, episodeLookup, cancellationToken) .ConfigureAwait(false); var hasNewEpisodes = false; var hasNewSeasons = false; if (series.ContainsEpisodesWithoutSeasonFolders) { hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false); } if (_config.Configuration.EnableInternetProviders) { hasNewEpisodes = await AddMissingEpisodes(series, seriesDataPath, episodeLookup, cancellationToken) .ConfigureAwait(false); } if (hasNewSeasons || hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved) { await series.RefreshMetadata(cancellationToken, true) .ConfigureAwait(false); await series.ValidateChildren(new Progress<double>(), cancellationToken, true) .ConfigureAwait(false); } }
/// <summary> /// Downloads the image from series. /// </summary> /// <param name="item">The item.</param> /// <param name="series">The series.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> private async Task DownloadImageFromSeries(BaseItem item, Series series, CancellationToken cancellationToken) { var tvdbPath = RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb)); var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); var url = FetchImageUrl(item, actorXmlPath, cancellationToken); if (!string.IsNullOrEmpty(url)) { url = TVUtils.BannerUrl + url; await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); } }
private async Task<TraktResponseDataContract> SendEpisodePlaystateUpdatesInternalAsync(List<object> episodesPayload, Series series, TraktUser traktUser, bool seen, CancellationToken cancellationToken) { var data = new { username = traktUser.UserName, password = traktUser.PasswordHash, imdb_id = series.GetProviderId(MetadataProviders.Imdb), tvdb_id = series.GetProviderId(MetadataProviders.Tvdb), title = series.Name, year = (series.ProductionYear ?? 0).ToString(CultureInfo.InvariantCulture), episodes = episodesPayload }; var options = new HttpRequestOptions { RequestContent = _jsonSerializer.SerializeToString(data), ResourcePool = Plugin.Instance.TraktResourcePool, CancellationToken = cancellationToken, Url = seen ? TraktUris.ShowEpisodeSeen : TraktUris.ShowEpisodeUnSeen }; var response = await _httpClient.Post(options).ConfigureAwait(false); return _jsonSerializer.DeserializeFromStream<TraktResponseDataContract>(response.Content); }
/// <summary> /// Add or remove a Show(Series) to/from the users trakt.tv library /// </summary> /// <param name="show">The show to remove</param> /// <param name="traktUser">The user who's library is being updated</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="eventType"></param> /// <returns>Task{TraktResponseDataContract}.</returns> public async Task<TraktResponseDataContract> SendLibraryUpdateAsync(Series show, TraktUser traktUser, CancellationToken cancellationToken, EventType eventType) { if (show == null) throw new ArgumentNullException("show"); if (traktUser == null) throw new ArgumentNullException("traktUser"); if (eventType == EventType.Update) return null; var data = new { username = traktUser.UserName, password = traktUser.PasswordHash, tvdb_id = show.GetProviderId(MetadataProviders.Tvdb), title = show.Name, year = show.ProductionYear }; var options = new HttpRequestOptions { RequestContent = _jsonSerializer.SerializeToString(data), ResourcePool = Plugin.Instance.TraktResourcePool, CancellationToken = cancellationToken }; switch (eventType) { case EventType.Add: break; case EventType.Remove: options.Url = TraktUris.ShowUnLibrary; break; } var response = await _httpClient.Post(options).ConfigureAwait(false); return _jsonSerializer.DeserializeFromStream<TraktResponseDataContract>(response.Content); }