/// <summary> /// Match the series information by Original Broadcast date /// </summary> private static bool MatchByBroadcastTime(VideoTags videoTags, string seriesID, bool dontOverwriteTitle, Log jobLog) { // If we have no original broadcasttime if (videoTags.OriginalBroadcastDateTime <= Globals.GlobalDefs.NO_BROADCAST_TIME) { jobLog.WriteEntry("No original broadcast date time", Log.LogEntryType.Debug); return(false); } // ************************************** // Get the series and episode information // ************************************** string lang = Localise.TwoLetterISO(); if (!((IList <string>)THETVDB_SUPPORTED_LANGUAGES).Contains(lang)) { lang = "en"; } string queryUrl = "http://www.thetvdb.com/api/" + MCEBUDDY_THETVDB_API_KEY + "/series/" + seriesID + "/all/" + lang + ".xml"; XPathDocument XpS; XPathNavigator NavS; XPathExpression ExpS; XPathNodeIterator ItrS; string overview = ""; string seriesName = ""; string bannerUrl = ""; string imdbID = ""; string firstAiredStr = ""; DateTime firstAired = GlobalDefs.NO_BROADCAST_TIME; int seasonNo = 0; int episodeNo = 0; string episodeName = ""; string episodeOverview = ""; string network = ""; DateTime premiereDate = GlobalDefs.NO_BROADCAST_TIME; List <string> genres = new List <string>(); try { // Get the Series information XpS = new XPathDocument(queryUrl); NavS = XpS.CreateNavigator(); ExpS = NavS.Compile("//Data/Series"); // Series information ItrS = NavS.Select(ExpS); ItrS.MoveNext(); seriesName = XML.GetXMLTagValue("SeriesName", ItrS.Current.OuterXml); overview = XML.GetXMLTagValue("Overview", ItrS.Current.OuterXml); if (String.IsNullOrWhiteSpace(bannerUrl = XML.GetXMLTagValue("poster", ItrS.Current.OuterXml))) // We get the poster first { if (String.IsNullOrWhiteSpace(bannerUrl = XML.GetXMLTagValue("fanart", ItrS.Current.OuterXml))) // We get the fanart next { bannerUrl = XML.GetXMLTagValue("banner", ItrS.Current.OuterXml); // We get the banner last } } imdbID = XML.GetXMLTagValue("IMDB_ID", ItrS.Current.OuterXml); network = XML.GetXMLTagValue("Network", ItrS.Current.OuterXml); DateTime.TryParse(XML.GetXMLTagValue("FirstAired", ItrS.Current.OuterXml), out premiereDate); string genreValue = XML.GetXMLTagValue("Genre", ItrS.Current.OuterXml); if (!String.IsNullOrWhiteSpace(genreValue)) { foreach (string genre in genreValue.Split('|')) { if (!String.IsNullOrWhiteSpace(genre)) { genres.Add(genre); } } } // Get the Episode information XpS = new XPathDocument(queryUrl); NavS = XpS.CreateNavigator(); ExpS = NavS.Compile("//Data/Episode"); ItrS = NavS.Select(ExpS); } catch (Exception e) { jobLog.WriteEntry("Unable to navigate TVDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } while (ItrS.MoveNext()) { firstAiredStr = XML.GetXMLTagValue("FirstAired", ItrS.Current.OuterXml); if (DateTime.TryParse(firstAiredStr, null, DateTimeStyles.AssumeLocal, out firstAired)) { if (firstAired <= GlobalDefs.NO_BROADCAST_TIME) { continue; } // The information is stored on the server using the network timezone // So we assume that the show being converted was recorded locally and is converted locally so the timezones match // Sometimes the timezones get mixed up so we check local time or universal time for a match if ((firstAired.Date == videoTags.OriginalBroadcastDateTime.ToLocalTime().Date) || // TVDB only reports the date not the time (firstAired.Date == videoTags.OriginalBroadcastDateTime.ToUniversalTime().Date)) { episodeName = XML.GetXMLTagValue("EpisodeName", ItrS.Current.OuterXml); if (String.IsNullOrWhiteSpace(episodeName)) { jobLog.WriteEntry("Empty episode name", Log.LogEntryType.Debug); return(false); // WRONG series, if there is no name we're in the incorrect series (probably wrong country) } int.TryParse(XML.GetXMLTagValue("SeasonNumber", ItrS.Current.OuterXml), out seasonNo); int.TryParse(XML.GetXMLTagValue("EpisodeNumber", ItrS.Current.OuterXml), out episodeNo); episodeOverview = XML.GetXMLTagValue("Overview", ItrS.Current.OuterXml); // ******************** // Get the banner file // ******************** VideoMetaData.DownloadBannerFile(videoTags, "http://www.thetvdb.com/banners/" + bannerUrl); // Get bannerfile if ((episodeNo != 0) && (videoTags.Episode == 0)) { videoTags.Episode = episodeNo; } if ((seasonNo != 0) && (videoTags.Season == 0)) { videoTags.Season = seasonNo; } if (!String.IsNullOrWhiteSpace(seriesName) && !dontOverwriteTitle) { videoTags.Title = seriesName; // Overwrite Series name since we matching by broadcast time and the name didn't match earlier so likely an issue with the name } if (!String.IsNullOrWhiteSpace(episodeName)) { videoTags.SubTitle = episodeName; // Overwrite episode name, it didn't match earlier in match by episode name, so it's probably wrong on the metadata } if (!String.IsNullOrWhiteSpace(episodeOverview)) { videoTags.Description = episodeOverview; // Overwrite } else if (!String.IsNullOrWhiteSpace(overview)) { videoTags.Description = overview; // Overwrite } if (!String.IsNullOrWhiteSpace(seriesID) && String.IsNullOrWhiteSpace(videoTags.tvdbId)) { videoTags.tvdbId = seriesID; } if (!String.IsNullOrWhiteSpace(imdbID) && String.IsNullOrWhiteSpace(videoTags.imdbId)) { videoTags.imdbId = imdbID; } if (!String.IsNullOrWhiteSpace(network) && String.IsNullOrWhiteSpace(videoTags.Network)) { videoTags.Network = network; } if (premiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > premiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TVDB dates, TVDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = premiereDate; // TVDB stores time in network (local) timezone } } if (genres.Count > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres.ToArray(); } } else { videoTags.Genres = genres.ToArray(); } } return(true); // Found a match got all the data, we're done here } } } jobLog.WriteEntry("No match found on TVDB for language " + lang, Log.LogEntryType.Warning); return(false); }
/// <summary> /// Match the series information by Episode name /// </summary> private static bool MatchByEpisodeName(VideoTags videoTags, string seriesID, Log jobLog) { if (String.IsNullOrWhiteSpace(videoTags.SubTitle)) { jobLog.WriteEntry("No episode name to match", Log.LogEntryType.Debug); return(false); //Nothing to match here } // ************************************** // Get the series and episode information // ************************************** foreach (string lang in THETVDB_SUPPORTED_LANGUAGES) // Cycle through all languages looking for a match since people in different countries/locales could be viewing shows recorded in different languages { jobLog.WriteEntry("Looking for Episode name match on TVDB using language " + lang, Log.LogEntryType.Debug); string queryUrl = "http://www.thetvdb.com/api/" + MCEBUDDY_THETVDB_API_KEY + "/series/" + seriesID + "/all/" + lang + ".xml"; XPathDocument XpS; XPathNavigator NavS; XPathExpression ExpS; XPathNodeIterator ItrS; string overview = ""; string bannerUrl = ""; string imdbID = ""; List <String> genres = new List <string>();; int seasonNo = 0; int episodeNo = 0; string episodeName = ""; string episodeOverview = ""; string network = ""; DateTime premiereDate = GlobalDefs.NO_BROADCAST_TIME; string firstAiredStr = ""; DateTime firstAired = GlobalDefs.NO_BROADCAST_TIME; try { // Get the Series information XpS = new XPathDocument(queryUrl); NavS = XpS.CreateNavigator(); ExpS = NavS.Compile("//Data/Series"); // Series information ItrS = NavS.Select(ExpS); ItrS.MoveNext(); overview = XML.GetXMLTagValue("Overview", ItrS.Current.OuterXml); if (String.IsNullOrWhiteSpace(bannerUrl = XML.GetXMLTagValue("poster", ItrS.Current.OuterXml))) // We get the poster first { if (String.IsNullOrWhiteSpace(bannerUrl = XML.GetXMLTagValue("fanart", ItrS.Current.OuterXml))) // We get the fanart next { bannerUrl = XML.GetXMLTagValue("banner", ItrS.Current.OuterXml); // We get the banner last } } imdbID = XML.GetXMLTagValue("IMDB_ID", ItrS.Current.OuterXml); network = XML.GetXMLTagValue("Network", ItrS.Current.OuterXml); DateTime.TryParse(XML.GetXMLTagValue("FirstAired", ItrS.Current.OuterXml), out premiereDate); string genreValue = XML.GetXMLTagValue("Genre", ItrS.Current.OuterXml); if (!String.IsNullOrWhiteSpace(genreValue)) { foreach (string genre in genreValue.Split('|')) { if (!String.IsNullOrWhiteSpace(genre)) { genres.Add(genre); } } } // Get the episode information XpS = new XPathDocument(queryUrl); NavS = XpS.CreateNavigator(); ExpS = NavS.Compile("//Data/Episode"); // Episode information ItrS = NavS.Select(ExpS); } catch (Exception e) { jobLog.WriteEntry("Unable to nagivate TMDB for language " + lang + "\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } while (ItrS.MoveNext()) { episodeName = XML.GetXMLTagValue("EpisodeName", ItrS.Current.OuterXml); if (!String.IsNullOrWhiteSpace(episodeName)) { if (String.Compare(videoTags.SubTitle.Trim(), episodeName.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0) // Compare the episode names (case / special characters / whitespace can change very often) { int.TryParse(XML.GetXMLTagValue("SeasonNumber", ItrS.Current.OuterXml), out seasonNo); int.TryParse(XML.GetXMLTagValue("EpisodeNumber", ItrS.Current.OuterXml), out episodeNo); episodeOverview = XML.GetXMLTagValue("Overview", ItrS.Current.OuterXml); // ******************** // Get the banner file // ******************** VideoMetaData.DownloadBannerFile(videoTags, "http://www.thetvdb.com/banners/" + bannerUrl); // Get bannerfile if ((episodeNo != 0) && (videoTags.Episode == 0)) { videoTags.Episode = episodeNo; } if ((seasonNo != 0) && (videoTags.Season == 0)) { videoTags.Season = seasonNo; } if (!String.IsNullOrWhiteSpace(episodeOverview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeOverview; } else if (!String.IsNullOrWhiteSpace(overview) && (String.IsNullOrWhiteSpace(videoTags.Description))) { videoTags.Description = overview; } if (!String.IsNullOrWhiteSpace(seriesID) && String.IsNullOrWhiteSpace(videoTags.tvdbId)) { videoTags.tvdbId = seriesID; } if (!String.IsNullOrWhiteSpace(imdbID) && String.IsNullOrWhiteSpace(videoTags.imdbId)) { videoTags.imdbId = imdbID; } if (!String.IsNullOrWhiteSpace(network) && String.IsNullOrWhiteSpace(videoTags.Network)) { videoTags.Network = network; } if (premiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > premiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TVDB dates, TVDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = premiereDate; // TVDB stores time in network (local) timezone } } if (genres.Count > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres.ToArray(); } } else { videoTags.Genres = genres.ToArray(); } } firstAiredStr = XML.GetXMLTagValue("FirstAired", ItrS.Current.OuterXml); if (DateTime.TryParse(firstAiredStr, null, DateTimeStyles.AssumeLocal, out firstAired)) { if (firstAired > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > firstAired.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TVDB dates, TVDB Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = firstAired; // TVDB stores time in network (local) timezone } } } return(true); // Found a match got all the data, we're done here } } } } jobLog.WriteEntry("No match found on TVDB for Episode Name", Log.LogEntryType.Debug); return(false); }
/// <summary> /// Supplements details by searching for a series show by title or IMDB ID /// Uses MyApiFilms /// </summary> /// <param name="matchByAirDate">If true, First match by airDate and then by Episode name and vice versa</param> /// <param name="videoTags">Video tags information to use and update</param> /// <param name="dontOverwriteTitle">True if the title has been manually corrected and not to be overwritten</param> /// <param name="offset">Initial search results offset</param> /// <returns>True if successful</returns> static private bool DownloadSeriesDetails(bool matchByAirDate, VideoTags videoTags, bool dontOverwriteTitle, Log jobLog, int offset = 0) { try { if (matchByAirDate && (videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME)) { jobLog.WriteEntry("Invalud original broadcast date to match on IMDB", Log.LogEntryType.Debug); return(false); // We can only match by airdate if there is something to match against (otherwise we get false positives) } else if (!matchByAirDate && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { jobLog.WriteEntry("Invalid episode name to match on IMDB", Log.LogEntryType.Debug); return(false); //Nothing to match here } jobLog.WriteEntry("Searching IMDB Series with result offset " + offset, Log.LogEntryType.Debug); List <MyApiFilms.SearchResults> searchResults = new List <MyApiFilms.SearchResults>(); if (String.IsNullOrWhiteSpace(videoTags.imdbId)) // If dont' have a specific imdb id specified, look up the series details { try { WebClient client = new WebClient(); client.Headers.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"); // Look for matching title string response = client.DownloadString(new Uri("http://www.myapifilms.com/imdb?title=" + videoTags.Title + "&format=JSON&aka=1&actors=S&seasons=1&limit=" + MY_API_SEARCH_LIMIT.ToString() + "&offset=" + offset.ToString())); searchResults = JsonConvert.DeserializeObject <List <MyApiFilms.SearchResults> >(response); } catch (Exception e) { jobLog.WriteEntry("Unable to connect to IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); // invalid JSON string } } else { // We have a series imdb id to match, use MyApiFilms to get the series details try { WebClient client = new WebClient(); client.Headers.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"); string response = client.DownloadString(new Uri("http://www.myapifilms.com/imdb?idIMDB=" + videoTags.imdbId + "&format=JSON&seasons=1&actors=S")); searchResults.Add(JsonConvert.DeserializeObject <MyApiFilms.SearchResults>(response)); } catch (Exception e) { jobLog.WriteEntry("Unable to connect to IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); // invalid JSON string } } foreach (MyApiFilms.SearchResults show in searchResults) // Cycle through all possible combinations { // Get and match the Series name string seriesName = show.title; if (String.IsNullOrWhiteSpace(videoTags.imdbId)) // Match names only if the movie imdb id is not forced, else take what is returned by moviedb { List <MyApiFilms.Aka> akaValues = show.akas; // check AKA names also - Also Known As (for language and localization) string title = videoTags.Title; bool akaMatch = false; if (akaValues != null) // Check if there are any AKA names to match { akaMatch = show.akas.Any(s => (String.Compare(s.title.Trim(), title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0 ? true : false)); } if ((String.Compare(seriesName.Trim(), videoTags.Title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) != 0) && (!akaMatch)) // ignore white space and special characters - check for both, Title and AKA names looking for a match { continue; // No match in name or AKA } } else if (!String.IsNullOrWhiteSpace(seriesName) && !dontOverwriteTitle) // make sure there is actually something returned here otherwise use default title { videoTags.Title = seriesName; // Take what is forced for the imdb id } DateTime seriesPremiereDate = GlobalDefs.NO_BROADCAST_TIME; DateTime.TryParseExact(show.releaseDate, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out seriesPremiereDate); // Look for the right Episode if (show.seasons != null) { foreach (MyApiFilms.Season season in show.seasons) { if (season.episodes != null) { foreach (MyApiFilms.Episode episode in season.episodes) { // Get the Episode name and match it string episodeName = episode.title; // Original broadcast date, some of them have an extra . in the date so get rid of it DateTime airDate = GlobalDefs.NO_BROADCAST_TIME; DateTime.TryParseExact(episode.date.Replace(".", "").Trim(), "d MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out airDate); if ((!matchByAirDate && (String.Compare(episodeName.Trim(), videoTags.SubTitle.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0)) || (matchByAirDate && (videoTags.OriginalBroadcastDateTime.ToLocalTime().Date == airDate.Date)) || (matchByAirDate && (videoTags.OriginalBroadcastDateTime.ToUniversalTime().Date == airDate.Date))) { // Get Genre's string[] genres = (show.genres == null ? null : show.genres.ToArray()); // Get Genres // Get Overview string episodeOverview = episode.plot; // Episode int episodeNo = episode.episode; // Season int seasonNo = season.numSeason; // IMDB Movie Id string imdbID = show.idIMDB; // Home free - update all the info where required if (matchByAirDate) // If we came in matching the Original Air Date - then we overwrite the episode details { // TODO: For now we only update subtitle and description if it is missing since IMDB is not upto date on TV series information yet. This needs to be changed and force updated once IMDB is complete // if (!String.IsNullOrWhiteSpace(episodeName)) videoTags.SubTitle = episodeName; // Overwrite // if (!String.IsNullOrWhiteSpace(episodeOverview)) videoTags.Description = episodeOverview; // Overwrite if (!String.IsNullOrWhiteSpace(episodeName) && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { videoTags.SubTitle = episodeName; } if (!String.IsNullOrWhiteSpace(episodeOverview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeOverview; } } else // only update what's missing { if (!String.IsNullOrWhiteSpace(episodeName) && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { videoTags.SubTitle = episodeName; } if (!String.IsNullOrWhiteSpace(episodeOverview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeOverview; } } if ((episodeNo != 0) && (videoTags.Episode == 0)) { videoTags.Episode = episodeNo; } if ((seasonNo != 0) && (videoTags.Season == 0)) { videoTags.Season = seasonNo; } if (!String.IsNullOrWhiteSpace(imdbID) && String.IsNullOrWhiteSpace(videoTags.imdbId)) { videoTags.imdbId = imdbID; } if (seriesPremiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > seriesPremiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use IMDB dates, IMDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = seriesPremiereDate; // IMDB stores time in network (local) timezone } } if (airDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > airDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use IMDB dates, IMDB Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = airDate; // IMDB stores time in network (local) timezone } } if (genres != null) { if (genres.Length > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres; } } else { videoTags.Genres = genres; } } } if (String.IsNullOrWhiteSpace(videoTags.MediaCredits)) // Get the media credits { videoTags.MediaCredits = ((show.actors != null) ? String.Join(";", show.actors.Select(s => s.actorName)) : ""); } if (String.IsNullOrWhiteSpace(videoTags.Rating)) // Get the ratings { videoTags.Rating = show.rated; } // Download the banner file VideoMetaData.DownloadBannerFile(videoTags, show.urlPoster); // Get bannerfile return(true); // Golden } } } } } } // Check if we have reached the limit for the results (currently 10 is the max returned in a single query), if so then check the next set of results if (searchResults.Count == MY_API_SEARCH_LIMIT) { return(DownloadSeriesDetails(matchByAirDate, videoTags, dontOverwriteTitle, jobLog, offset + MY_API_SEARCH_LIMIT)); } jobLog.WriteEntry("No match found on IMDB Series", Log.LogEntryType.Debug); return(false); } catch (Exception e) { jobLog.WriteEntry("Unable to initialize IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } }
/// <summary> /// Download the information about the show from TV.com /// </summary> /// <param name="matchByAirDate">True to match the Episode by Original AirDate, False to match by Episode Name</param> /// <param name="dontOverwriteTitle">True if the title has been manually corrected and not to be overwritten</param> /// <returns>True if found a match</returns> static private bool DownloadSeriesDetails(bool matchByAirDate, VideoTags videoTags, bool dontOverwriteTitle, Log jobLog) { HtmlDocument htmlDoc; try { if (matchByAirDate && (videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME)) { jobLog.WriteEntry("Invalid original broadcast date to match on TV.com", Log.LogEntryType.Debug); return(false); // We can only match by airdate if there is something to match against (otherwise we get false positives) } else if (!matchByAirDate && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { jobLog.WriteEntry("Invalid episode name to match on TV.com", Log.LogEntryType.Debug); return(false); //Nothing to match here } htmlDoc = new HtmlWeb().Load("http://www.tv.com/search?q=" + videoTags.Title); // Get the matching shows list HtmlNodeCollection nodes = htmlDoc.DocumentNode.SelectNodes("//li[@class='result show']"); foreach (HtmlNode node in nodes) { string seriesTitle = "", seriesLink = "", bannerUrl = ""; HtmlDocument subDoc = new HtmlDocument(); subDoc.Load(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(node.InnerHtml))); // Get the banner URL HtmlNode urlNode = subDoc.DocumentNode.SelectSingleNode("//div[@class='mask']//img[@src]"); if (urlNode != null) { if (urlNode.HasAttributes) { bannerUrl = urlNode.Attributes["src"].Value.Trim(); // URL } else { bannerUrl = ""; // reset each cycle } } else { bannerUrl = ""; // reset each cycle } // Get the series name and link to page HtmlNode subNode = subDoc.DocumentNode.SelectSingleNode("//div[@class='info']//h4//a[@href]"); if (subNode != null) { seriesTitle = subNode.InnerText.Trim(); // Get the title of the series // Compare the series title with the title of the recording if (String.Compare(seriesTitle.Trim(), videoTags.Title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0) { HtmlNode subNode1 = subDoc.DocumentNode.SelectSingleNode("//ul[@class='sub_links _inline_navigation']"); if (subNode1 != null) { HtmlDocument subDoc1 = new HtmlDocument(); subDoc1.Load(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(subNode1.InnerHtml))); HtmlNode subNode2 = subDoc1.DocumentNode.SelectSingleNode("//li//a[@href]"); if (subNode2 != null) { if (subNode2.HasAttributes) { seriesLink = subNode2.Attributes["href"].Value; // Get the link for the episodes in the series // Now the links to the various season pages HtmlDocument sDoc = new HtmlWeb().Load("http://www.tv.com" + seriesLink); // Get the premiere date HtmlNode pNode = sDoc.DocumentNode.SelectSingleNode("//span[@class='divider']"); int start = pNode.InnerText.IndexOf("Premiered") + "Premiered".Length; int length = pNode.InnerText.IndexOf("In") - start; string premiereString = pNode.InnerText.Substring(start, length).Trim(); DateTime premiereDate = GlobalDefs.NO_BROADCAST_TIME; DateTime.TryParse(premiereString, out premiereDate); // Get the seasons HtmlNodeCollection sNodes = sDoc.DocumentNode.SelectNodes("//li[@class='filter ']//a[@href]"); foreach (HtmlNode sNode in sNodes) // go through each season { string seasonLink; // Now extract the link to the season episodes page if (sNode.HasAttributes) { seasonLink = sNode.Attributes["href"].Value; // the href has the link to the season page // Now the links to the various season pages HtmlDocument eDoc = new HtmlWeb().Load("http://www.tv.com" + seasonLink); HtmlNodeCollection eNodes = eDoc.DocumentNode.SelectNodes("//div[@class='no_toggle_wrapper _clearfix']"); foreach (HtmlNode eNode in eNodes) // Now extract the episode names, original air dates and compare { string episodeName = "", episodeDesc = ""; DateTime airDate = GlobalDefs.NO_BROADCAST_TIME; int episodeNo = 0, seasonNo = 0; HtmlNode tempNode; HtmlDocument tempDoc = new HtmlDocument(); tempDoc.Load(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(eNode.InnerHtml))); // Extract the season number tempNode = eDoc.DocumentNode.SelectSingleNode("//li[@class='filter selected']"); if (tempNode != null) { if (tempNode.HasAttributes) { int.TryParse(tempNode.Attributes["data-season"].Value.Trim(), out seasonNo); // Season number } } // Extract the episode name tempNode = tempDoc.DocumentNode.SelectSingleNode("//a[@class='title']"); if (tempNode != null) { episodeName = tempNode.InnerText.Trim(); // Episode Name } // Extract the episode number tempNode = tempDoc.DocumentNode.SelectSingleNode("//div[@class='ep_info']"); if (tempNode != null) { int.TryParse(tempNode.InnerText.Trim().Replace("Episode", ""), out episodeNo); // Episode number } // Extract the original broadcast date tempNode = tempDoc.DocumentNode.SelectSingleNode("//div[@class='date']"); if (tempNode != null) { DateTime.TryParse(tempNode.InnerText.Trim(), null, DateTimeStyles.AssumeLocal, out airDate); // Air Date } // Extract the episode description tempNode = tempDoc.DocumentNode.SelectSingleNode("//div[@class='description']"); if (tempNode != null) { episodeDesc = tempNode.InnerText.Trim(); // Episode description } // Now match and store - match either episode name or air date // The information is stored on the server using the network timezone // So we assume that the show being converted was recorded locally and is converted locally so the timezones match // Sometimes the timezones get mixed up so we check local time or universal time for a match if ((!matchByAirDate && (String.Compare(episodeName.Trim(), videoTags.SubTitle.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0)) || (matchByAirDate && (videoTags.OriginalBroadcastDateTime.ToLocalTime().Date == airDate.Date)) || (matchByAirDate && (videoTags.OriginalBroadcastDateTime.ToUniversalTime().Date == airDate.Date))) { // Home free - update all the info where required if (matchByAirDate) // If we came in matching the Original Air Date - then we overwrite the episode details { if (!String.IsNullOrWhiteSpace(episodeName)) { videoTags.SubTitle = episodeName; // Overwrite } if (!String.IsNullOrWhiteSpace(episodeDesc)) { videoTags.Description = episodeDesc; // Overwrite } } else // only update what's missing { if (!String.IsNullOrWhiteSpace(episodeName) && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { videoTags.SubTitle = episodeName; } if (!String.IsNullOrWhiteSpace(episodeDesc) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeDesc; } } if ((episodeNo != 0) && (videoTags.Episode == 0)) { videoTags.Episode = episodeNo; } if ((seasonNo != 0) && (videoTags.Season == 0)) { videoTags.Season = seasonNo; } if (airDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > airDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TV dates, TV Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = airDate; // TV stores time in network (local) timezone } } if (premiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > premiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use IMDB dates, IMDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = premiereDate; // IMDB stores time in network (local) timezone } } VideoMetaData.DownloadBannerFile(videoTags, bannerUrl); // Get bannerfile // All Good now return(true); } } } } } } } } } } jobLog.WriteEntry("No match found on TV.com", Log.LogEntryType.Debug); return(false); } catch (Exception e) { jobLog.WriteEntry("Unable to connect to TV.com\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } }
/// <summary> /// Supplements details by searching for a movie by title or IMDB ID /// Uses MyApiFilms /// </summary> /// <param name="videoTags">Video tags information to use and update</param> /// <param name="dontOverwriteTitle">True if the title has been manually corrected and not to be overwritten</param> /// <param name="offset">Initial search results offset</param> /// <returns></returns> static public bool DownloadMovieDetails(VideoTags videoTags, bool dontOverwriteTitle, Log jobLog, int offset = 0) { try { jobLog.WriteEntry("Searching IMDB Movie with result offset " + offset, Log.LogEntryType.Debug); List <MyApiFilms.SearchResults> searchResults = new List <MyApiFilms.SearchResults>(); if (String.IsNullOrWhiteSpace(videoTags.imdbId)) // If dont' have a specific movieId specified, look up the movie details { try { WebClient client = new WebClient(); client.Headers.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"); // Look for matching title string response = client.DownloadString(new Uri("http://www.myapifilms.com/imdb?title=" + videoTags.Title + "&format=JSON&aka=1&actors=S&limit=" + MY_API_SEARCH_LIMIT.ToString() + "&offset=" + offset.ToString())); searchResults = JsonConvert.DeserializeObject <List <MyApiFilms.SearchResults> >(response); } catch (Exception e) { jobLog.WriteEntry("Unable to connect to IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); // invalid JSON string } } else { // We have a series imdb id to match, use MyApiFilms to get the series details try { WebClient client = new WebClient(); client.Headers.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"); string response = client.DownloadString(new Uri("http://www.myapifilms.com/imdb?idIMDB=" + videoTags.imdbId + "&format=JSON&actors=S")); searchResults.Add(JsonConvert.DeserializeObject <MyApiFilms.SearchResults>(response)); } catch (Exception e) { jobLog.WriteEntry("Unable to connect to IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); // invalid JSON string } } foreach (MyApiFilms.SearchResults movie in searchResults) // Cycle through all possible combinations { // Get and match Movie name string movieName = movie.title; if (String.IsNullOrWhiteSpace(videoTags.imdbId)) // Match names only if the movie imdb id is not forced, else take what is returned by moviedb { List <MyApiFilms.Aka> akaValues = movie.akas; // check AKA names also - Also Known As (for language and localization) string title = videoTags.Title; bool akaMatch = false; if (akaValues != null) // Check if there are any AKA names to match { akaMatch = movie.akas.Any(s => (String.Compare(s.title.Trim(), title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0 ? true : false)); } if ((String.Compare(movieName.Trim(), videoTags.Title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) != 0) && (!akaMatch)) // ignore white space and special characters - check for both, Title and AKA names looking for a match { continue; // No match in name or AKA } // Match year if available if (videoTags.OriginalBroadcastDateTime > GlobalDefs.NO_BROADCAST_TIME) // If we have a time, we can try to narrow the search parameters { // The information is stored on the server using the network timezone // So we assume that the show being converted was recorded locally and is converted locally so the timezones match DateTime dt = videoTags.OriginalBroadcastDateTime.ToLocalTime(); if (movie.year.Trim() != dt.Year.ToString()) { continue; } } videoTags.imdbId = movie.idIMDB; // since IMDB movie is not forced, get it here } else if (!String.IsNullOrWhiteSpace(movieName) && !dontOverwriteTitle) // make sure there is actually something returned here otherwise use default title { videoTags.Title = movieName; // Take what is forced for the imdb movie id } videoTags.IsMovie = true; // Get Overview string overview = movie.simplePlot; if (!String.IsNullOrWhiteSpace(overview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = overview; } // Get original release date DateTime releaseDate = GlobalDefs.NO_BROADCAST_TIME; string released = movie.releaseDate; if (DateTime.TryParseExact(released, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out releaseDate)) { if (releaseDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > releaseDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use IMDB dates, IMDB Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = releaseDate; // IMDB stores time in network (local) timezone } } } string[] genres = (movie.genres == null ? null : movie.genres.ToArray()); // Get Genres if (genres != null) { if (genres.Length > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres; } } else { videoTags.Genres = genres; } } } if (String.IsNullOrWhiteSpace(videoTags.MediaCredits)) // Get the media credits { videoTags.MediaCredits = ((movie.actors != null) ? String.Join(";", movie.actors.Select(s => s.actorName)) : ""); } if (String.IsNullOrWhiteSpace(videoTags.Rating)) // Get the ratings { videoTags.Rating = movie.rated; } // Download the banner file VideoMetaData.DownloadBannerFile(videoTags, movie.urlPoster); // Get bannerfile return(true); // We found it, get out home free } // Check if we have reached the limit for the results (currently 10 is the max returned in a single query), if so then check the next set of results if (searchResults.Count == MY_API_SEARCH_LIMIT) { return(DownloadMovieDetails(videoTags, dontOverwriteTitle, jobLog, offset + MY_API_SEARCH_LIMIT)); } jobLog.WriteEntry("No match found on IMDB", Log.LogEntryType.Debug); return(false); } catch (Exception e) { jobLog.WriteEntry("Unable to initialize IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } }
private const int MY_API_SEARCH_LIMIT = 10; // Max number of results that can be returned in a single search /// <summary> /// Downloads the information for a movie or series episode (no matching) given the IMDB ID for the movie or episode (not show) /// Uses OMDBApi /// </summary> /// <param name="videoTags">Video Tags structure with the IMDB ID</param> /// <returns>True if successful</returns> static public bool BootStrapByIMDBId(VideoTags videoTags, Log jobLog) { try { OMDBApi.SearchResult result = new OMDBApi.SearchResult(); if (String.IsNullOrWhiteSpace(videoTags.imdbId)) // do we have a valid ID to begin with { return(false); } try { WebClient client = new WebClient(); client.Headers.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"); string response = client.DownloadString(new Uri("http://www.omdbapi.com/?i=" + videoTags.imdbId + "&r=json")); result = JsonConvert.DeserializeObject <OMDBApi.SearchResult>(response); } catch (Exception e) { jobLog.WriteEntry("Unable to bootstrap from IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); // invalid JSON string } if (String.IsNullOrWhiteSpace(result.Title)) // Check if got a valid result { jobLog.WriteEntry("Unable to boot strap, IMDB returned empty Title", Log.LogEntryType.Debug); return(false); } // Check if it's a movie if (result.Type.ToLower().Contains("movie")) { videoTags.Title = result.Title; // Take what is forced for the imdb movie id videoTags.IsMovie = true; // Get Overview string overview = result.Plot; if (!String.IsNullOrWhiteSpace(overview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = overview; } // Get original release date DateTime releaseDate = GlobalDefs.NO_BROADCAST_TIME; if (DateTime.TryParseExact(result.Released, "dd MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out releaseDate)) { if (releaseDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > releaseDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use IMDB dates, IMDB Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = releaseDate; // IMDB stores time in network (local) timezone } } } string[] genres = result.Genre.Split(','); // Get Genres if (genres != null) { if (genres.Length > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres; } } else { videoTags.Genres = genres; } } } // Download the banner file VideoMetaData.DownloadBannerFile(videoTags, result.Poster); // Get bannerfile return(true); // We found it, get out home free } else // Process as a series { DateTime seriesPremiereDate = GlobalDefs.NO_BROADCAST_TIME; DateTime.TryParseExact(result.Released, "dd MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out seriesPremiereDate); string episodeName = result.Title; // Title here is the episode name since we are forcing a IMDB ID leading directly to a episode (the Showname is taken from the filename or metadata) string bannerUrl = result.Poster; // Get Poster URL string[] genres = result.Genre.Split(','); // Get Genres string episodeOverview = result.Plot; // Get Overview if (!String.IsNullOrWhiteSpace(episodeName) && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { videoTags.SubTitle = episodeName; } if (!String.IsNullOrWhiteSpace(episodeOverview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeOverview; } else if (!String.IsNullOrWhiteSpace(episodeOverview) && (String.IsNullOrWhiteSpace(videoTags.Description))) { videoTags.Description = episodeOverview; } if (seriesPremiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > seriesPremiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use IMDB dates, IMDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = seriesPremiereDate; // IMDB stores time in network (local) timezone } } if (genres != null) { if (genres.Length > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres; } } else { videoTags.Genres = genres; } } } // Check if it's a sport series if (videoTags.Genres != null) { if (videoTags.Genres.Length > 0) { if (videoTags.Genres.Contains("sport", StringComparer.OrdinalIgnoreCase)) { videoTags.IsSports = true; } } } // Download the banner file VideoMetaData.DownloadBannerFile(videoTags, bannerUrl); // Get bannerfile return(true); // Golden } } catch (Exception e) { jobLog.WriteEntry("Unable to use IMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } }
/// <summary> /// Match the series information by Episode name /// </summary> private static bool MatchByEpisodeName(TMDbClient client, VideoTags videoTags, TvShow tvShow, Log jobLog) { if (String.IsNullOrWhiteSpace(videoTags.SubTitle)) { jobLog.WriteEntry("Invalid episode name to match", Log.LogEntryType.Debug); return(false); //Nothing to match here } // Cycle through all Seasons and Episodes looking for a match for (int sNo = 0; sNo <= tvShow.NumberOfSeasons; sNo++) { TvSeason season = client.GetTvSeasonAsync(tvShow.Id, sNo).Result; if (season == null || season.Episodes == null) { continue; } for (int eNo = 0; eNo <= season.Episodes.Count; eNo++) { TvEpisode episode = client.GetTvEpisodeAsync(tvShow.Id, sNo, eNo).Result; if (episode == null) { continue; } string episodeName = episode.Name; if (!String.IsNullOrWhiteSpace(episodeName)) { if (String.Compare(videoTags.SubTitle.Trim(), episodeName.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0) // Compare the episode names (case / special characters / whitespace can change very often) { int episodeNo = episode.EpisodeNumber; int seasonNo = season.SeasonNumber; string episodeOverview = episode.Overview; string overview = season.Overview; string tvdbID = (episode.ExternalIds != null ? (episode.ExternalIds.TvdbId != null ? episode.ExternalIds.TvdbId.ToString() : "") : ""); string imdbID = (episode.ExternalIds != null ? episode.ExternalIds.ImdbId : ""); string tmdbID = (tvShow.Id != 0 ? tvShow.Id.ToString() : ""); string network = (tvShow.Networks != null ? String.Join(";", tvShow.Networks.Select(s => s.Name)) : ""); DateTime premiereDate = GlobalDefs.NO_BROADCAST_TIME; if (tvShow.FirstAirDate != null) { premiereDate = (DateTime)tvShow.FirstAirDate; } List <string> genres = (tvShow.Genres != null ? tvShow.Genres.Select(s => s.Name).ToList() : new List <string>()); DateTime firstAired = (DateTime)(episode.AirDate != null ? episode.AirDate : GlobalDefs.NO_BROADCAST_TIME); string mediaCredits = (tvShow.Credits != null ? ((tvShow.Credits.Cast != null) ? String.Join(";", tvShow.Credits.Cast.Select(s => s.Name)) : "") : ""); client.GetConfig(); // First we need to get the config VideoMetaData.DownloadBannerFile(videoTags, client.GetImageUrl("original", tvShow.PosterPath).OriginalString); // Get bannerfile if ((episodeNo != 0) && (videoTags.Episode == 0)) { videoTags.Episode = episodeNo; } if ((seasonNo != 0) && (videoTags.Season == 0)) { videoTags.Season = seasonNo; } if (!String.IsNullOrWhiteSpace(episodeOverview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeOverview; } else if (!String.IsNullOrWhiteSpace(overview) && (String.IsNullOrWhiteSpace(videoTags.Description))) { videoTags.Description = overview; } if (!String.IsNullOrWhiteSpace(tvdbID) && String.IsNullOrWhiteSpace(videoTags.tvdbId)) { videoTags.tvdbId = tvdbID; } if (!String.IsNullOrWhiteSpace(imdbID) && String.IsNullOrWhiteSpace(videoTags.imdbId)) { videoTags.imdbId = imdbID; } if (!String.IsNullOrWhiteSpace(tmdbID) && String.IsNullOrWhiteSpace(videoTags.tmdbId)) { videoTags.tmdbId = tmdbID; } if (!String.IsNullOrWhiteSpace(network) && String.IsNullOrWhiteSpace(videoTags.Network)) { videoTags.Network = network; } if (!String.IsNullOrWhiteSpace(mediaCredits) && String.IsNullOrWhiteSpace(videoTags.MediaCredits)) { videoTags.MediaCredits = mediaCredits; } if (premiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > premiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TVDB dates, TVDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = premiereDate; // TVDB stores time in network (local) timezone } } if (genres.Count > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres.ToArray(); } } else { videoTags.Genres = genres.ToArray(); } } if (firstAired > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > firstAired.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TVDB dates, TVDB Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = firstAired; // TVDB stores time in network (local) timezone } } return(true); // Found a match got all the data, we're done here } } } } jobLog.WriteEntry("No match found by episode name on TMDB", Log.LogEntryType.Debug); return(false); // nothing matches }
static public bool DownloadMovieDetails(VideoTags videoTags, bool dontOverwriteTitle, Log jobLog) { // The new v3 database is accessed via the TMDbLib API's try { TMDbClient client = new TMDbClient(MCEBUDDY_TMDB_APIKey); // List<SearchMovie> movieSearch = new List<SearchMovie>(); SearchContainer <SearchMovie> movieSearch = new SearchContainer <SearchMovie>(); Movie movieMatch = null; // TODO: Add support for multiple language searches if (String.IsNullOrWhiteSpace(videoTags.imdbId)) // If dont' have a specific movieId specified, look up the movie details { if (videoTags.OriginalBroadcastDateTime > GlobalDefs.NO_BROADCAST_TIME) // Release date narrow down { // The information is stored on the server using the network timezone // So we assume that the show being converted was recorded locally and is converted locally so the timezones match DateTime dt = videoTags.OriginalBroadcastDateTime.ToLocalTime(); movieSearch = client.SearchMovieAsync(videoTags.Title.Trim().ToLower(), 0, true, dt.Year).Result; } else // Title Check { movieSearch = client.SearchMovieAsync(videoTags.Title.Trim().ToLower(), 0, true, 0).Result; } } else // Specific ID { movieMatch = client.GetMovieAsync(videoTags.imdbId).Result; // We have a specific movie to work with } if (movieMatch == null) // If we haven't forced a movie match { foreach (SearchMovie movieResult in movieSearch.Results) // Cycle through all possible combinations { Movie movie = client.GetMovieAsync(movieResult.Id).Result; List <AlternativeTitle> akaValues = null; if (movie.AlternativeTitles != null) { akaValues = movie.AlternativeTitles.Titles; } bool akaMatch = false; string title = videoTags.Title; if (akaValues != null) // Check if there are any AKA names to match { akaMatch = akaValues.Any(s => (String.Compare(s.Title.Trim(), title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) == 0 ? true : false)); } // Get and match Movie name (check both titles and aka values) if (String.Compare(movie.Title.Trim(), videoTags.Title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) != 0) // ignore white space and special characters { if (String.Compare(movie.OriginalTitle.Trim(), videoTags.Title.Trim(), CultureInfo.InvariantCulture, (CompareOptions.IgnoreSymbols | CompareOptions.IgnoreCase)) != 0) // ignore white space and special characters { if (!akaMatch) // check for aka value matches { continue; // No match in name } } } // If we got here, then we found a match movieMatch = movie; break; // We are done here } } if (movieMatch != null) // We have a match { if (!String.IsNullOrWhiteSpace(videoTags.imdbId) && !dontOverwriteTitle) // Match names only if the movie imdb id is not forced, else take what is returned by moviedb { videoTags.Title = movieMatch.Title; // Take what is forced for the imdb movie id } // Get Movie Id videoTags.tmdbId = movieMatch.Id.ToString(); videoTags.IsMovie = true; // this is a movie // Get Overview string overview = movieMatch.Overview; if (!String.IsNullOrWhiteSpace(overview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = overview; } // Get original release date if (movieMatch.ReleaseDate != null) { DateTime releaseDate = (DateTime)movieMatch.ReleaseDate; if (releaseDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.OriginalBroadcastDateTime.Date > releaseDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use MovieDB dates, MovieDB Dates are more reliable than video metadata usually { videoTags.OriginalBroadcastDateTime = releaseDate; // MovieDB stores time in network (local) timezone } } } // Get Genres List <string> genres = new List <string>(); foreach (Genre genre in movieMatch.Genres) { genres.Add(genre.Name); } if (genres.Count > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres.ToArray(); } } else { videoTags.Genres = genres.ToArray(); } } // Download the banner file client.GetConfig(); // First we need to get the config VideoMetaData.DownloadBannerFile(videoTags, client.GetImageUrl("original", movieMatch.PosterPath).OriginalString); // Get bannerfile return(true); // home free, we're good } jobLog.WriteEntry("No match found in TMDB", Log.LogEntryType.Debug); return(false); } catch (Exception e) { jobLog.WriteEntry("Unable to connect to TMDB\r\nError -> " + e.ToString(), Log.LogEntryType.Warning); return(false); } }
/// <summary> /// Match the series information by Original Broadcast date /// </summary> private static bool MatchByBroadcastTime(TMDbClient client, VideoTags videoTags, TvShow tvShow, bool dontOverwriteTitle, Log jobLog) { if (videoTags.OriginalBroadcastDateTime <= GlobalDefs.NO_BROADCAST_TIME) { jobLog.WriteEntry("Invalid original broadcast date to match", Log.LogEntryType.Debug); return(false); //Nothing to match here } // Cycle through all Seasons and Episodes looking for a match for (int sNo = 0; sNo <= tvShow.NumberOfSeasons; sNo++) { TvSeason season = client.GetTvSeason(tvShow.Id, sNo); if (season == null || season.Episodes == null) { continue; } for (int eNo = 0; eNo <= season.Episodes.Count; eNo++) { TvEpisode episode = client.GetTvEpisode(tvShow.Id, sNo, eNo); if (episode == null) { continue; } string episodeName = episode.Name; if (!String.IsNullOrWhiteSpace(episodeName)) { DateTime firstAired = episode.AirDate; if (firstAired == null || firstAired <= GlobalDefs.NO_BROADCAST_TIME) { continue; } if ((firstAired.Date == videoTags.OriginalBroadcastDateTime.ToLocalTime().Date) || // TMDB only reports the date not the time (firstAired.Date == videoTags.OriginalBroadcastDateTime.ToUniversalTime().Date)) { int episodeNo = episode.EpisodeNumber; int seasonNo = season.SeasonNumber; string episodeOverview = episode.Overview; string overview = season.Overview; string tvdbID = (episode.ExternalIds != null ? (episode.ExternalIds.TvdbId != null ? episode.ExternalIds.TvdbId.ToString() : "") : ""); string imdbID = (episode.ExternalIds != null ? episode.ExternalIds.ImdbId : ""); string tmdbID = (tvShow.Id != 0 ? tvShow.Id.ToString() : ""); string network = (tvShow.Networks != null ? String.Join(";", tvShow.Networks.Select(s => s.Name)) : ""); DateTime premiereDate = GlobalDefs.NO_BROADCAST_TIME; if (tvShow.FirstAirDate != null) { premiereDate = (DateTime)tvShow.FirstAirDate; } List <string> genres = (tvShow.Genres != null ? tvShow.Genres.Select(s => s.Name).ToList() : new List <string>()); string mediaCredits = (tvShow.Credits != null ? ((tvShow.Credits.Cast != null) ? String.Join(";", tvShow.Credits.Cast.Select(s => s.Name)) : "") : ""); string seriesName = tvShow.Name; client.GetConfig(); // First we need to get the config VideoMetaData.DownloadBannerFile(videoTags, client.GetImageUrl("original", tvShow.PosterPath).OriginalString); // Get bannerfile // TODO: At what point do we go from supplementing to being primary? // Since TVDB is primary we only supplement data if ((episodeNo != 0) && (videoTags.Episode == 0)) { videoTags.Episode = episodeNo; } if ((seasonNo != 0) && (videoTags.Season == 0)) { videoTags.Season = seasonNo; } if (!String.IsNullOrWhiteSpace(seriesName) && String.IsNullOrWhiteSpace(videoTags.Title) && !dontOverwriteTitle) { videoTags.Title = seriesName; } if (!String.IsNullOrWhiteSpace(episodeName) && String.IsNullOrWhiteSpace(videoTags.SubTitle)) { videoTags.SubTitle = episodeName; } if (!String.IsNullOrWhiteSpace(episodeOverview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = episodeOverview; } else if (!String.IsNullOrWhiteSpace(overview) && String.IsNullOrWhiteSpace(videoTags.Description)) { videoTags.Description = overview; } if (!String.IsNullOrWhiteSpace(tvdbID) && String.IsNullOrWhiteSpace(videoTags.tvdbId)) { videoTags.tvdbId = tvdbID; } if (!String.IsNullOrWhiteSpace(imdbID) && String.IsNullOrWhiteSpace(videoTags.imdbId)) { videoTags.imdbId = imdbID; } if (!String.IsNullOrWhiteSpace(tmdbID) && String.IsNullOrWhiteSpace(videoTags.tmdbId)) { videoTags.tmdbId = tmdbID; } if (!String.IsNullOrWhiteSpace(network) && String.IsNullOrWhiteSpace(videoTags.Network)) { videoTags.Network = network; } if (!String.IsNullOrWhiteSpace(mediaCredits) && String.IsNullOrWhiteSpace(videoTags.MediaCredits)) { videoTags.MediaCredits = mediaCredits; } if (premiereDate > GlobalDefs.NO_BROADCAST_TIME) { if ((videoTags.SeriesPremiereDate <= GlobalDefs.NO_BROADCAST_TIME) || (videoTags.SeriesPremiereDate.Date > premiereDate.Date)) // Sometimes the metadata from the video recordings are incorrect and report the recorded date (which is more recent than the release date) then use TVDB dates, TVDB Dates are more reliable than video metadata usually { videoTags.SeriesPremiereDate = premiereDate; // TVDB stores time in network (local) timezone } } if (genres.Count > 0) { if (videoTags.Genres != null) { if (videoTags.Genres.Length == 0) { videoTags.Genres = genres.ToArray(); } } else { videoTags.Genres = genres.ToArray(); } } return(true); // Found a match got all the data, we're done here } } } } jobLog.WriteEntry("No match found for broadcast date on TMDB", Log.LogEntryType.Debug); return(false); // nothing matches }