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> >());
     }
 }