public override int DiscoverSubCategories(Category parentCategory) { parentCategory.SubCategories = new List<Category>(); #region Help if (parentCategory.IsHelpState()) throw new OnlineVideosException("Forum: http://tinyurl.com/ov-netflix"); #endregion #region Profiles if (parentCategory.IsProfilesState()) { foreach (JToken profile in profiles) { Category profileCat = new Category() { Name = profile["profileName"].Value<string>(), HasSubCategories = true, Thumb = profile["avatar"]["images"]["100"].Value<string>(), ParentCategory = parentCategory }; profileCat.SetState(NetflixUtils.ProfileState); profileCat.SetProfile(profile); parentCategory.SubCategories.Add(profileCat); } } else if (parentCategory.IsProfileState()) { Settings.Categories.Clear(); Settings.DynamicCategoriesDiscovered = false; currentProfile = parentCategory.GetProfile(); parentCategory.ParentCategory.Name = ProfileCategoryName; MyGetWebData(string.Format(switchProfileUrl, apiRoot, ProfileToken)); if (rememberStartupUserProfile) { List<OnlineVideos.Reflection.FieldPropertyDescriptorByRef> props = GetUserConfigurationProperties(); OnlineVideos.Reflection.FieldPropertyDescriptorByRef prop = props.First(p => p.DisplayName == "Startup user profile"); this.SetConfigValueFromString(prop, ProfileName); startupUserProfile = ProfileName; } throw new OnlineVideosException(CangedToProfile); } #endregion #region Home Categories else if (parentCategory.IsHomeCategoriesState()) { string data = MyGetWebData((parentCategory as RssLink).Url); List<Category> cats = GetHomeCategories(data, parentCategory); parentCategory.SubCategories = cats; } #endregion #region Kids else if (parentCategory.IsKidsState()) { string data = MyGetWebData((parentCategory as RssLink).Url); HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(data); RssLink characters = new RssLink() { Name = "Characters", HasSubCategories = true, SubCategories = new List<Category>(), ParentCategory = parentCategory }; //Well use regex in this case... Regex rgx = new Regex(@"title=\\\""(?<name>[^\\]*)\\\"".href=\\\""(?<url>[^\\]*)(?:[^<]*)<img.src=\\""(?<thumb>[^\\]*)"); foreach (Match m in rgx.Matches(data)) { RssLink character = new RssLink() { Name = m.Groups["name"].Value, Url = m.Groups["url"].Value, Thumb = m.Groups["thumb"].Value, ParentCategory = characters, HasSubCategories = true }; character.SetState(NetflixUtils.SinglePageCategoriesState); characters.SubCategories.Add(character); } characters.SubCategoriesDiscovered = characters.SubCategories.Count > 0; parentCategory.SubCategories.Add(characters); foreach (HtmlNode a in doc.DocumentNode.SelectNodes("//a[contains(@href, 'netflix.com/KidsAltGenre')]")) { string url = a.GetAttributeValue("href", ""); if (!parentCategory.SubCategories.Any(c => (c as RssLink).Url == url)) { RssLink category = new RssLink() { Name = a.InnerText.Trim(), Url = a.GetAttributeValue("href", ""), HasSubCategories = true, ParentCategory = parentCategory }; category.SetState(NetflixUtils.SinglePageCategoriesState); parentCategory.SubCategories.Add(category); } } } #endregion #region SinglePageCategories else if (parentCategory.IsSinglePageCategoriesState()) { foreach (NetflixData d in GetSinglePageNetflixData(parentCategory, enableAddRemoveMylist)) { RssLink cat = new RssLink() { Name = d.Title, Url = d.Id, Thumb = d.Cover, ParentCategory = parentCategory, HasSubCategories = !(parentCategory.ParentCategory != null && parentCategory.ParentCategory.Name == "Characters"), Description = d.Description }; cat.SetState(NetflixUtils.TitleState); if (!string.IsNullOrEmpty(d.TrackId)) cat.SetTrackId(d.TrackId); parentCategory.SubCategories.Add(cat); } } #endregion #region MultiplePageCategories else if (parentCategory.IsMultiplePageCategoriesState()) //This is browse categories { string url = (parentCategory as RssLink).Url; string data = MyGetWebData(url); SetShaktiApiAndBuildId(data); Uri uri = new Uri(url); string agid = HttpUtility.ParseQueryString(uri.Query).Get("agid"); if (!string.IsNullOrEmpty(ShaktiApi) && !string.IsNullOrEmpty(BuildId) && !string.IsNullOrEmpty(agid)) { data = MyGetWebData(string.Format(genreUrl, ShaktiApi, BuildId, agid, 0, 50, BrowseSort)); JObject json = (JObject)JsonConvert.DeserializeObject(data); foreach (JObject item in json["catalogItems"]) { RssLink category = new RssLink() { Name = (string)item["title"], Url = ((int)item["titleId"]).ToString(), Thumb = (string)item["boxart"], ParentCategory = parentCategory, HasSubCategories = true, Description = enableDesc && enableDescInListing ? GetTitleDescription(((int)item["titleId"]).ToString(), ((int)item["trackId"]).ToString()) : "" }; category.SetState(NetflixUtils.TitleState); if (enableDesc || enableAddRemoveMylist) category.SetTrackId(((int)item["trackId"]).ToString()); parentCategory.SubCategories.Add(category); } if (parentCategory.SubCategories.Count >= 50) { NextPageCategory next = new NextPageCategory() { Url = string.Format(genreUrl, ShaktiApi, BuildId, agid, "START_INDEX", "STOP_INDEX", BrowseSort), ParentCategory = parentCategory }; next.SetState(NetflixUtils.TitleState); next.SetStartIndex("51"); parentCategory.SubCategories.Add(next); } } } #endregion #region Title else if (parentCategory.IsTitleState()) { string data = MyGetWebData(movieUrl + (parentCategory as RssLink).Url); Regex rgx = new Regex(@"nf\.constants\.page\.contextData =(.*); }\(netflix\)\);"); Match m = rgx.Match(data); if (m.Success) { string jsonData = m.Groups[1].Value; JObject json = (JObject)JsonConvert.DeserializeObject(jsonData); #region Series if ((bool)json["displayPage"]["data"]["isShow"]) { #region Multiple seasons if (data.Contains("class=\"seasonItem")) { HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(data); foreach (HtmlNode li in doc.DocumentNode.SelectNodes("//li[starts-with(@class, 'seasonItem')]")) { HtmlNode a = li.SelectSingleNode("a"); RssLink season = new RssLink() { Name = "Season " + a.InnerText, Url = string.Format(seasonDetailsUrl, (parentCategory as RssLink).Url, a.GetAttributeValue("data-vid", "")), HasSubCategories = true, ParentCategory = parentCategory, Thumb = parentCategory.Thumb }; season.SetState(NetflixUtils.EpisodesState); parentCategory.SubCategories.Add(season); } } #endregion #region Single season else if (data.Contains("data-episodeid=\"")) { HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(data); HtmlNode docNode = doc.DocumentNode; HtmlNode seasonNode = docNode.SelectSingleNode("//span[@class = 'selectorTxt']"); string season = seasonNode == null ? "1x" : (seasonNode.InnerText + "x"); HtmlNode episodes = docNode.SelectSingleNode("//ul[@class = 'episodeList']"); foreach (HtmlNode episode in episodes.SelectNodes("li")) { string seqNum = episode.SelectSingleNode("span[@class = 'seqNum']").InnerText; HtmlNode pDesc = enableDesc ? episode.SelectSingleNode(".//p[@class = 'synopsis']") : null; parentCategory.SubCategories.Add(new RssLink() { Name = season + (seqNum.Count() < 2 ? ("0" + seqNum) : seqNum) + " " + episode.SelectSingleNode("span[@class = 'episodeTitle']").InnerText, Url = episode.GetAttributeValue("data-episodeid", ""), ParentCategory = parentCategory, Thumb = parentCategory.Thumb, Description = pDesc != null ? pDesc.InnerText : "", HasSubCategories = false }); } } #endregion #region Netflix title else { data = MyGetWebData(string.Format(netflixOrgEpisodes, apiRoot, (parentCategory as RssLink).Url, latestAuthUrl)); rgx = new Regex(@"({""title"":[^$]*)"); m = rgx.Match(data); if (m.Success) { data = m.Groups[1].Value; json = (JObject)JsonConvert.DeserializeObject(data); foreach (JArray seasonArray in json["episodes"]) { foreach (JToken episode in seasonArray.Where(e => e["availableForED"] == null || e["availableForED"].Value<bool>())) { int e = (int)episode["episode"]; int s = (int)episode["season"]; RssLink cat = new RssLink() { Name = s + "x" + (e < 10 ? ("0" + e) : e.ToString()) + " " + (string)episode["title"], ParentCategory = parentCategory, Thumb = parentCategory.Thumb, HasSubCategories = false, Url = episode["episodeId"].ToString() }; JToken stills = episode["stills"]; if (stills != null && stills.Count() > 0) { cat.Thumb = stills.Last()["url"].Value<string>(); } if (enableDesc) { cat.Description = episode["synopsis"].Value<string>(); } parentCategory.SubCategories.Add(cat); } } parentCategory.SubCategories.Sort((c1, c2) => c1.Name.CompareTo(c2.Name)); } #endregion } } #endregion #region Movies else { parentCategory.SubCategories.Add(new RssLink() { Description = GetTitleDescription((parentCategory as RssLink).Url, parentCategory.GetTrackId()), Name = parentCategory.Name, Url = (parentCategory as RssLink).Url, Thumb = parentCategory.Thumb, ParentCategory = parentCategory, HasSubCategories = false }); } #endregion } } #endregion #region Episodes else { string jsonData = MyGetWebData((parentCategory as RssLink).Url); JObject json = (JObject)JsonConvert.DeserializeObject(jsonData); string data = (string)json["html"]; HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(data); HtmlNode docNode = doc.DocumentNode; HtmlNode episodes = docNode.SelectSingleNode("//ul[@class = 'episodeList']"); foreach (HtmlNode episode in episodes.SelectNodes("li")) { string seqNum = episode.SelectSingleNode("span[@class = 'seqNum']").InnerText; HtmlNode pDesc = enableDesc ? episode.SelectSingleNode(".//p[@class = 'synopsis']") : null; parentCategory.SubCategories.Add(new RssLink() { Name = parentCategory.Name.Replace("Season", "") + "x" + (seqNum.Count() < 2 ? ("0" + seqNum) : seqNum) + " " + episode.SelectSingleNode("span[@class = 'episodeTitle']").InnerText, Url = episode.GetAttributeValue("data-episodeid", ""), ParentCategory = parentCategory, Thumb = parentCategory.Thumb, Description = pDesc != null ? pDesc.InnerText : "", HasSubCategories = false }); } } #endregion parentCategory.SubCategoriesDiscovered = parentCategory.RememberDiscoveredItems() && parentCategory.SubCategories.Count > 0; return parentCategory.SubCategories.Count; }