private static async Task <IEnumerable <IGrouping <string, ShowTime> > > InnerReadShowTimesFromWebAsync(Theater theaterFromDb) { HtmlDocument marketsDocument = await InternetHelpers.GetPageHtmlDocumentAsync(theaterFromDb.CalendarUrl); HtmlNode showTimeControllerNode = marketsDocument.DocumentNode.SelectSingleNode("//div[@ng-controller='ShowtimeController']"); Regex showTimesRegex = new Regex(@"initCalendar\('([^']+)','([^']+)'\)"); Match showTimesMatch = showTimesRegex.Match(showTimeControllerNode.Attributes["ng-init"].Value); if (!showTimesMatch.Success) { _logger.Error("Show times did not have expected format {ShowTimeController}", showTimeControllerNode.OuterHtml); throw new Exception("Show times did not have expected format"); } string showTimesUrlBase = showTimesMatch.Groups[1].Value; string showTimesUrlCode = showTimesMatch.Groups[2].Value; string ajaxUrl = $"{showTimesUrlBase}calendar/{showTimesUrlCode}"; string jsonContent = await InternetHelpers.GetPageContentAsync(ajaxUrl); JToken json = JToken.Parse(jsonContent, new JsonLoadSettings()); // https://drafthouse.com/austin/tickets/showtime/0002/29212 //IEnumerable<IGrouping<string, ShowTime>> movies = if (json["Calendar"]["Cinemas"] == null) { _logger.Warn("No show time cinemas for {Theater}", theaterFromDb.Name); return(Enumerable.Empty <IGrouping <string, ShowTime> >()); } IEnumerable <IGrouping <string, ShowTime> > showTimesByMovie = from cinemaToken in json["Calendar"]["Cinemas"] from monthsNode in cinemaToken["Months"] from weeksNode in monthsNode["Weeks"] from daysNode in weeksNode["Days"] where daysNode["Films"] != null from filmsNode in daysNode["Films"] from seriesNode in filmsNode["Series"] from formatsNode in seriesNode["Formats"] from sessionsNode in formatsNode["Sessions"] let cinemaSlug = cinemaToken["MarketSlug"]?.Value <string>() let cinemaId = cinemaToken["CinemaId"]?.Value <string>() let title = filmsNode["FilmName"]?.Value <string>() let movieTitle = title let showTimeDateTime = sessionsNode["SessionDateTime"]?.Value <DateTime>() let showTimeId = sessionsNode["SessionId"]?.Value <string>() let showTimeStatus = sessionsNode["SessionStatus"]?.Value <string>() let seatsLeft = sessionsNode["SeatsLeft"]?.Value <int>() let showTimeUrl = $"https://drafthouse.com/{cinemaSlug}/tickets/showtime/{cinemaId}/{showTimeId}" let showTime = new ShowTime(theaterFromDb, showTimeUrl, showTimeDateTime, showTimeStatus, seatsLeft) group showTime by movieTitle into movieGroup select movieGroup; return(showTimesByMovie); }
private static async Task <IEnumerable <IGrouping <string, ShowTime> > > ReadShowTimesFromWebAsync(Theater theaterFromDb) { try { _logger.Info("Loading movies for {Theater}", theaterFromDb.Name); // Sometimes the browser will return the page source before the page is fully loaded, in those // cases just retry until you get something. return(await PolicyHelpers.RetryPolicy.ExecuteAsync(async() => await InnerReadShowTimesFromWebAsync(theaterFromDb))); } catch (Exception ex) { _logger.Error(ex, "Retry limit exceeded when reading showtimes for {Theater}", theaterFromDb.Name); return(Enumerable.Empty <IGrouping <string, ShowTime> >()); } }