private static void BackgroundDownloader_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { foreach (var serviceLogo in StationLogosToDownload) { var logoPath = serviceLogo.Value[0]; if (DownloadSdLogo(serviceLogo.Value[1], logoPath) && string.IsNullOrEmpty(serviceLogo.Key.LogoImage)) { serviceLogo.Key.mxfGuideImage = SdMxf.GetGuideImage("file://" + logoPath, GetStringEncodedImage(logoPath)); if (File.Exists(logoPath)) { // update dimensions var image = Image.FromFile(logoPath); serviceLogo.Key.extras["logo"].Height = image.Height; serviceLogo.Key.extras["logo"].Width = image.Width; } } if (!BackgroundDownloader.CancellationPending) { continue; } e.Cancel = true; break; } }
private static void InitializeKeywordGroups() { foreach (var group in Groups) { SdMxf.GetKeywordGroup(group, "m1"); } }
private static void UpdateSeriesAirdate(string seriesId, DateTime airdate) { // write the mxf entry SdMxf.GetSeriesInfo(seriesId.Substring(2, 8)).StartAirdate = airdate.ToString("yyyy-MM-dd"); // update cache if needed try { using (var reader = new StringReader(epgCache.GetAsset(seriesId))) { var serializer = new JsonSerializer(); var cached = (GenericDescription)serializer.Deserialize(reader, typeof(GenericDescription)); if (!string.IsNullOrEmpty(cached.StartAirdate)) { return; } cached.StartAirdate = airdate.Equals(DateTime.MinValue) ? "" : airdate.ToString("yyyy-MM-dd"); using (var writer = new StringWriter()) { serializer.Serialize(writer, cached); epgCache.UpdateAssetJsonEntry(seriesId, writer.ToString()); } } } catch { // ignored } }
private static void ProcessSeriesDescriptionsResponses() { // process request response foreach (var response in seriesDescriptionResponses) { ++processedObjects; ReportProgress(); var seriesId = response.Key; var description = response.Value; // determine which seriesInfo this belongs to var mxfSeriesInfo = SdMxf.GetSeriesInfo(seriesId.Substring(2, 8)); // populate descriptions mxfSeriesInfo.ShortDescription = description.Description100; mxfSeriesInfo.Description = description.Description1000; // serialize JSON directly to a file using (var writer = new StringWriter()) { try { var serializer = new JsonSerializer(); serializer.Serialize(writer, description); epgCache.AddAsset(seriesId, writer.ToString()); } catch { // ignored } } } }
private static bool CreateXmltvFile() { try { xmltv = new xmltv { Date = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture), SourceInfoUrl = "http://schedulesdirect.org", SourceInfoName = "Schedules Direct", GeneratorInfoName = "EPG123", GeneratorInfoUrl = "http://epg123.garyan2.net", Channels = new List <XmltvChannel>(), Programs = new List <XmltvProgramme>() }; foreach (var service in SdMxf.With.Services) { xmltv.Channels.Add(BuildXmltvChannel(service)); if (service.MxfScheduleEntries.ScheduleEntry.Count == 0 && config.XmltvAddFillerData) { // add a program specific for this service var program = new MxfProgram { Description = config.XmltvFillerProgramDescription, IsGeneric = true, Title = service.Name, ProgramId = $"EPG123FILL{service.StationId}" }; // populate the schedule entries var startTime = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 0, 0, 0); var stopTime = startTime + TimeSpan.FromDays(config.DaysToDownload); do { service.MxfScheduleEntries.ScheduleEntry.Add(new MxfScheduleEntry { Duration = config.XmltvFillerProgramLength * 60 * 60, mxfProgram = SdMxf.GetProgram(program.ProgramId, program), StartTime = startTime, IsRepeat = true }); startTime += TimeSpan.FromHours(config.XmltvFillerProgramLength); } while (startTime < stopTime); } foreach (var scheduleEntry in service.MxfScheduleEntries.ScheduleEntry) { xmltv.Programs.Add(BuildXmltvProgram(scheduleEntry, $"EPG123.{service.StationId}.schedulesdirect.org")); } } return(true); } catch (Exception ex) { Logger.WriteInformation("Failed to create the XMLTV file. Message : " + ex.Message); } return(false); }
private static void ProcessSeriesImageResponses() { // process request response foreach (var response in imageResponses) { ++processedObjects; ReportProgress(); var uid = response.ProgramId; if (response.Data == null) { continue; } MxfSeriesInfo series = null; if (response.ProgramId.StartsWith("SP")) { foreach (var key in sportsSeries.AllKeys) { if (!sportsSeries.Get(key).Contains(response.ProgramId)) { continue; } series = SdMxf.GetSeriesInfo(key); uid = key; } } else { series = SdMxf.GetSeriesInfo(response.ProgramId.Substring(2, 8)); } if (series == null || !string.IsNullOrEmpty(series.GuideImage) || series.extras.ContainsKey("artwork")) { continue; } // get series images var artwork = GetTieredImages(response.Data, new List <string> { "series", "sport", "episode" }); if (response.ProgramId.StartsWith("SP") && artwork.Count <= 0) { continue; } series.extras.Add("artwork", artwork); series.mxfGuideImage = GetGuideImageAndUpdateCache(artwork, ImageType.Series, uid); } }
private static void DetermineEpisodeInfo(MxfProgram mxfProgram, SchedulesDirect.Program sdProgram) { if (sdProgram.EntityType != "Episode") { return; } // use the last 4 numbers as a production number mxfProgram.EpisodeNumber = int.Parse(mxfProgram.ProgramId.Substring(10)); if (sdProgram.Metadata != null) { // grab season and episode numbers if available foreach (var providers in sdProgram.Metadata) { ProgramMetadataProvider provider = null; if (config.TheTvdbNumbers) { if (providers.TryGetValue("TheTVDB", out provider) || providers.TryGetValue("TVmaze", out provider)) { if (provider.SeasonNumber == 0 && provider.EpisodeNumber == 0) { continue; } } } if (provider == null && !providers.TryGetValue("Gracenote", out provider)) { continue; } mxfProgram.SeasonNumber = provider.SeasonNumber; mxfProgram.EpisodeNumber = provider.EpisodeNumber; } } // if there is a season number, create a season entry if (mxfProgram.SeasonNumber != 0) { mxfProgram.mxfSeason = SdMxf.GetSeason(mxfProgram.mxfSeriesInfo.SeriesId, mxfProgram.SeasonNumber, (sdProgram.HasSeasonArtwork || sdProgram.HasEpisodeArtwork) ? mxfProgram.ProgramId : null); } }
private static MxfGuideImage GetGuideImageAndUpdateCache(List <ProgramArtwork> artwork, ImageType type, string cacheKey = null) { if (artwork.Count == 0) { if (cacheKey != null) { epgCache.UpdateAssetImages(cacheKey, string.Empty); } return(null); } if (cacheKey != null) { using (var writer = new StringWriter()) { var serializer = new JsonSerializer(); serializer.Serialize(writer, artwork); epgCache.UpdateAssetImages(cacheKey, writer.ToString()); } } ProgramArtwork image = null; if (type == ImageType.Movie) { image = artwork.FirstOrDefault(); } else if (config.SeriesPosterArt || config.SeriesWsArt) { image = artwork.SingleOrDefault(arg => arg.Aspect.ToLower().Equals(config.SeriesPosterArt ? "2x3" : "16x9")); } else { image = artwork.SingleOrDefault(arg => arg.Aspect.ToLower().Equals("4x3")); } if (image == null && type == ImageType.Series) { image = artwork.SingleOrDefault(arg => arg.Aspect.ToLower().Equals("4x3")); } return(image != null?SdMxf.GetGuideImage(Helper.Standalone?image.Uri : image.Uri.Replace($"{SdApi.JsonBaseUrl}{SdApi.JsonApi}", $"http://{Environment.MachineName}:{Helper.TcpPort}/")) : null); }
private static List <MxfPersonRank> GetPersons(List <sdProgramPerson> persons, string[] roles) { if (persons == null) { return(null); } var personName = new List <string>(); var ret = new List <MxfPersonRank>(); foreach (var person in persons.Where(person => roles.Any(role => person.Role.ToLower().Contains(role.ToLower()) && !personName.Contains(person.Name)))) { ret.Add(new MxfPersonRank { mxfPerson = SdMxf.GetPersonId(person.Name), Rank = int.Parse(person.BillingOrder), Character = person.CharacterName }); personName.Add(person.Name); } return(ret); }
private static void ProcessProgramResponses() { // process request response foreach (var sdProgram in programResponses) { ++processedObjects; ReportProgress(); // determine which program this belongs to var mxfProgram = SdMxf.GetProgram(sdProgram.ProgramId); // build a standalone program BuildMxfProgram(mxfProgram, sdProgram); // add JSON to cache if (sdProgram.Md5 != null) { mxfProgram.extras["md5"] = sdProgram.Md5; using (var writer = new StringWriter()) { try { var serializer = new JsonSerializer(); serializer.Serialize(writer, sdProgram); epgCache.AddAsset(sdProgram.Md5, writer.ToString()); } catch { // ignored } } } else { Logger.WriteWarning($"Did not cache program {mxfProgram.ProgramId} due to missing Md5 hash."); } } }
private static void ProcessMovieImageResponses() { // process request response foreach (var response in imageResponses) { ++processedObjects; ReportProgress(); if (response.Data == null) { continue; } // determine which program this belongs to var mxfProgram = SdMxf.GetProgram(response.ProgramId); // first choice is return from Schedules Direct List <ProgramArtwork> artwork; artwork = GetTieredImages(response.Data, new List <string> { "episode" }).Where(arg => arg.Aspect.Equals("2x3")).ToList(); // second choice is from TMDb if allowed and available if (artwork.Count == 0 || artwork[0].Category.Equals("Staple") && config.TMDbCoverArt && tmdbApi.IsAlive) { var tmdb = GetTmdbMoviePoster(mxfProgram.Title, mxfProgram.Year, mxfProgram.Language); if (tmdb.Count > 0) { artwork = tmdb; } } // regardless if image is found or not, store the final result in xml file // this avoids hitting the tmdb server every update for every movie missing cover art mxfProgram.extras.Add("artwork", artwork); mxfProgram.mxfGuideImage = GetGuideImageAndUpdateCache(artwork, ImageType.Movie, mxfProgram.extras["md5"]); } }
private static bool GetMd5ScheduleEntries(string[] dates, int start) { // reject 0 requests if (SdMxf.With.Services.Count - start < 1) { return(true); } // build request for station schedules var requests = new ScheduleRequest[Math.Min(SdMxf.With.Services.Count - start, MaxQueries / dates.Length)]; for (var i = 0; i < requests.Length; ++i) { requests[i] = new ScheduleRequest() { StationId = SdMxf.With.Services[start + i].StationId, Date = dates }; } // request schedule md5s from Schedules Direct var stationResponses = SdApi.GetScheduleMd5s(requests); if (stationResponses == null) { return(false); } // build request of daily schedules not downloaded yet var newRequests = new List <ScheduleRequest>(); foreach (var request in requests) { var requestErrors = new Dictionary <int, string>(); var mxfService = SdMxf.GetService(request.StationId); if (stationResponses.TryGetValue(request.StationId, out var stationResponse)) { // if the station return is empty, go to next station if (stationResponse.Count == 0) { var comment = $"Failed to parse the schedule Md5 return for stationId {mxfService.StationId} ({mxfService.CallSign}) on {dates[0]} and after."; if (CheckSuppressWarnings(mxfService.CallSign)) { Logger.WriteInformation(comment); } else { Logger.WriteWarning(comment); } processedObjects += dates.Length; ReportProgress(); continue; } // scan through all the dates returned for the station and request dates that are not cached var newDateRequests = new List <string>(); var dupeMd5s = new HashSet <string>(); foreach (var day in dates) { if (stationResponse.TryGetValue(day, out var dayResponse) && (dayResponse.Code == 0) && !string.IsNullOrEmpty(dayResponse.Md5)) { var filepath = $"{Helper.Epg123CacheFolder}\\{SafeFilename(dayResponse.Md5)}"; var file = new FileInfo(filepath); if (file.Exists && (file.Length > 0) && !epgCache.JsonFiles.ContainsKey(dayResponse.Md5)) { using (var reader = File.OpenText(filepath)) { epgCache.AddAsset(dayResponse.Md5, reader.ReadToEnd()); } } if (epgCache.JsonFiles.ContainsKey(dayResponse.Md5)) { ++processedObjects; ReportProgress(); ++cachedSchedules; } else { newDateRequests.Add(day); } if (!ScheduleEntries.ContainsKey(dayResponse.Md5)) { ScheduleEntries.Add(dayResponse.Md5, new[] { request.StationId, day }); } else { var previous = ScheduleEntries[dayResponse.Md5][1]; var comment = $"Duplicate schedule Md5 return for stationId {mxfService.StationId} ({mxfService.CallSign}) on {day} with {previous}."; Logger.WriteWarning(comment); dupeMd5s.Add(dayResponse.Md5); } } else if ((dayResponse != null) && (dayResponse.Code != 0) && !requestErrors.ContainsKey(dayResponse.Code)) { requestErrors.Add(dayResponse.Code, dayResponse.Message); } } // clear out dupe entries foreach (var dupe in dupeMd5s) { var previous = ScheduleEntries[dupe][1]; var comment = $"Removing duplicate Md5 schedule entry for stationId {mxfService.StationId} ({mxfService.CallSign}) on {previous}."; Logger.WriteWarning(comment); ScheduleEntries.Remove(dupe); } // create the new request for the station if (newDateRequests.Count > 0) { newRequests.Add(new ScheduleRequest() { StationId = request.StationId, Date = newDateRequests.ToArray() }); } } else { // requested station was not in response Logger.WriteWarning($"Requested stationId {mxfService.StationId} ({mxfService.CallSign}) was not present in schedule Md5 response."); processedObjects += dates.Length; ReportProgress(); continue; } if (requestErrors.Count <= 0) { continue; } foreach (var keyValuePair in requestErrors) { Logger.WriteError($"Requests for MD5 schedule entries of station {request.StationId} returned error code {keyValuePair.Key} , message: {keyValuePair.Value}"); } } ReportProgress(); // download the remaining daily schedules to the cache directory if (newRequests.Count > 0) { // request daily schedules from Schedules Direct var responses = SdApi.GetScheduleListings(newRequests.ToArray()); if (responses == null) { return(false); } // process the responses foreach (var response in responses) { ++processedObjects; ReportProgress(); if (response?.Programs == null) { continue; } ++downloadedSchedules; // serialize JSON directly to a file if (ScheduleEntries.TryGetValue(response.Metadata.Md5, out var serviceDate)) { using (var writer = new StringWriter()) { try { var serializer = new JsonSerializer(); serializer.Serialize(writer, response); epgCache.AddAsset(response.Metadata.Md5, writer.ToString()); } catch { Logger.WriteInformation($"Failed to write station daily schedule file to cache file. station: {serviceDate[0]} ; date: {serviceDate[1]}"); } } } else { try { var compare = ScheduleEntries .Where(arg => arg.Value[0].Equals(response.StationId)) .Single(arg => arg.Value[1].Equals(response.Metadata.StartDate)); Logger.WriteWarning($"Md5 mismatch for station {compare.Value[0]} on {compare.Value[1]}. Expected: {compare.Key} - Downloaded {response.Metadata.Md5}"); } catch { Logger.WriteWarning($"Md5 mismatch for station {response.StationId} on {response.Metadata.StartDate}. Downloaded {response.Metadata.Md5}"); } } } } ReportProgress(); return(true); }
private static void ProcessMd5ScheduleEntry(string md5) { // ensure cached file exists if (!epgCache.JsonFiles.ContainsKey(md5)) { return; } // read the cached file ScheduleResponse schedule; try { using (var reader = new StringReader(epgCache.GetAsset(md5))) { var serializer = new JsonSerializer(); schedule = (ScheduleResponse)serializer.Deserialize(reader, typeof(ScheduleResponse)); if (schedule == null) { Logger.WriteError("Failed to read Md5Schedule file in cache directory."); return; } } } catch (Exception ex) { Logger.WriteError("Error occurred when trying to read Md5Schedule file in cache directory. Message: " + ex.Message); return; } // determine which service entry applies to var mxfService = SdMxf.GetService(schedule.StationId); // process each program schedule entry foreach (var scheduleProgram in schedule.Programs) { // prepopulate some of the program var mxfProgram = SdMxf.GetProgram(scheduleProgram.ProgramId); if (mxfProgram.extras.Count == 0) { mxfProgram.ProgramId = scheduleProgram.ProgramId; mxfProgram.UidOverride = $"{scheduleProgram.ProgramId.Substring(0, 10)}_{scheduleProgram.ProgramId.Substring(10)}"; mxfProgram.extras.Add("md5", scheduleProgram.Md5); if (scheduleProgram.Multipart?.PartNumber > 0) { mxfProgram.extras.Add("multipart", $"{scheduleProgram.Multipart.PartNumber}/{scheduleProgram.Multipart.TotalParts}"); } if (config.OadOverride && scheduleProgram.New) { mxfProgram.extras.Add("newAirDate", scheduleProgram.AirDateTime.ToLocalTime()); } } mxfProgram.IsSeasonFinale |= Helper.StringContains(scheduleProgram.IsPremiereOrFinale, "Season Finale"); mxfProgram.IsSeasonPremiere |= Helper.StringContains(scheduleProgram.IsPremiereOrFinale, "Season Premiere"); mxfProgram.IsSeriesFinale |= Helper.StringContains(scheduleProgram.IsPremiereOrFinale, "Series Finale"); mxfProgram.IsSeriesPremiere |= Helper.StringContains(scheduleProgram.IsPremiereOrFinale, "Series Premiere"); if (!mxfProgram.extras.ContainsKey("premiere")) { mxfProgram.extras.Add("premiere", false); } if (scheduleProgram.Premiere) { mxfProgram.extras["premiere"] = true; // used only for movie and miniseries premieres } // grab any tvratings from desired countries var scheduleTvRatings = new Dictionary <string, string>(); if (scheduleProgram.Ratings != null) { var ratings = config.RatingsOrigin.Split(','); foreach (var rating in scheduleProgram.Ratings.Where(rating => string.IsNullOrEmpty(rating.Country) || Helper.TableContains(ratings, "ALL") || Helper.TableContains(ratings, rating.Country))) { scheduleTvRatings.Add(rating.Body, rating.Code); } } // populate the schedule entry and create program entry as required mxfService.MxfScheduleEntries.ScheduleEntry.Add(new MxfScheduleEntry { AudioFormat = EncodeAudioFormat(scheduleProgram.AudioProperties), Duration = scheduleProgram.Duration, Is3D = Helper.TableContains(scheduleProgram.VideoProperties, "3d"), IsBlackout = scheduleProgram.SubjectToBlackout, IsClassroom = scheduleProgram.CableInTheClassroom, IsCc = Helper.TableContains(scheduleProgram.AudioProperties, "cc"), IsDelay = Helper.StringContains(scheduleProgram.LiveTapeDelay, "delay"), IsDvs = Helper.TableContains(scheduleProgram.AudioProperties, "dvs"), IsEnhanced = Helper.TableContains(scheduleProgram.VideoProperties, "enhanced"), IsFinale = Helper.StringContains(scheduleProgram.IsPremiereOrFinale, "finale"), IsHdtv = CheckHdOverride(schedule.StationId) || !CheckSdOverride(schedule.StationId) && Helper.TableContains(scheduleProgram.VideoProperties, "hdtv"), //IsHdtvSimulCast = null, IsInProgress = scheduleProgram.JoinedInProgress, IsLetterbox = Helper.TableContains(scheduleProgram.VideoProperties, "letterbox"), IsLive = Helper.StringContains(scheduleProgram.LiveTapeDelay, "live"), //IsLiveSports = null, IsPremiere = scheduleProgram.Premiere || Helper.StringContains(scheduleProgram.IsPremiereOrFinale, "premiere"), IsRepeat = !scheduleProgram.New, IsSap = Helper.TableContains(scheduleProgram.AudioProperties, "sap"), IsSubtitled = Helper.TableContains(scheduleProgram.AudioProperties, "subtitled"), IsTape = Helper.StringContains(scheduleProgram.LiveTapeDelay, "tape"), Part = scheduleProgram.Multipart?.PartNumber ?? 0, Parts = scheduleProgram.Multipart?.TotalParts ?? 0, mxfProgram = mxfProgram, StartTime = scheduleProgram.AirDateTime, //TvRating is determined in the class itself to combine with the program content ratings IsSigned = scheduleProgram.Signed }); mxfService.MxfScheduleEntries.ScheduleEntry.Last().extras.Add("ratings", scheduleTvRatings); } }
private static void DetermineSeriesInfo(MxfProgram mxfProgram, SchedulesDirect.Program sdProgram) { // for sports programs that start with "SP", create a series entry based on program title // this groups them all together as a series for recordings MxfSeriesInfo mxfSeriesInfo; if (mxfProgram.ProgramId.StartsWith("SP")) { var name = mxfProgram.Title.Replace(' ', '_'); mxfSeriesInfo = SdMxf.GetSeriesInfo(name); sportsSeries.Add(name, mxfProgram.ProgramId); } else { // create a seriesInfo entry if needed mxfSeriesInfo = SdMxf.GetSeriesInfo(mxfProgram.ProgramId.Substring(2, 8), mxfProgram.ProgramId); if (!mxfSeriesInfo.extras.ContainsKey("tvdb") && sdProgram.Metadata != null) { foreach (var providers in sdProgram.Metadata) { if (providers.TryGetValue("TheTVDB", out var provider)) { mxfSeriesInfo.extras.Add("tvdb", provider.SeriesId.ToString()); } } } if (mxfProgram.ProgramId.StartsWith("SH")) { // go ahead and create/update the cache entry as needed if (epgCache.JsonFiles.ContainsKey(mxfProgram.ProgramId)) { try { using (var reader = new StringReader(epgCache.GetAsset(mxfProgram.ProgramId))) { var serializer = new JsonSerializer(); var cached = (GenericDescription)serializer.Deserialize(reader, typeof(GenericDescription)); if (cached.StartAirdate == null) { cached.StartAirdate = mxfProgram.OriginalAirdate ?? string.Empty; using (var writer = new StringWriter()) { serializer.Serialize(writer, cached); epgCache.UpdateAssetJsonEntry(mxfProgram.ProgramId, writer.ToString()); } } } } catch { // ignored } } else { var newEntry = new GenericDescription { Code = 0, Description1000 = mxfProgram.IsGeneric ? mxfProgram.Description : null, Description100 = mxfProgram.IsGeneric ? mxfProgram.ShortDescription : null, StartAirdate = mxfProgram.OriginalAirdate ?? string.Empty }; using (var writer = new StringWriter()) { var serializer = new JsonSerializer(); serializer.Serialize(writer, newEntry); epgCache.AddAsset(mxfProgram.ProgramId, writer.ToString()); } } } } mxfSeriesInfo.Title = mxfSeriesInfo.Title ?? mxfProgram.Title; mxfProgram.mxfSeriesInfo = mxfSeriesInfo; }
private static void DetermineProgramKeywords(MxfProgram mxfProgram, SchedulesDirect.Program sdProgram) { // determine primary group of program var group = keygroups.UNKNOWN; if (mxfProgram.IsMovie) { group = keygroups.MOVIES; } else if (mxfProgram.IsPaidProgramming) { group = keygroups.PAIDPROGRAMMING; } else if (mxfProgram.IsSports) { group = keygroups.SPORTS; } else if (mxfProgram.IsKids) { group = keygroups.KIDS; } else if (mxfProgram.IsEducational) { group = keygroups.EDUCATIONAL; } else if (mxfProgram.IsNews) { group = keygroups.NEWS; } else if (mxfProgram.IsSpecial) { group = keygroups.SPECIAL; } else if (mxfProgram.IsReality) { group = keygroups.REALITY; } else if (mxfProgram.IsSeries) { group = keygroups.SERIES; } // build the keywords/categories if (group == keygroups.UNKNOWN) { return; } var mxfKeyGroup = SdMxf.GetKeywordGroup(Groups[(int)group]); mxfProgram.mxfKeywords.Add(new MxfKeyword { Index = mxfKeyGroup.Index, Word = Groups[(int)group] }); // add premiere categories as necessary if (mxfProgram.IsSeasonPremiere || mxfProgram.IsSeriesPremiere) { var premiere = SdMxf.GetKeywordGroup(Groups[(int)keygroups.PREMIERES]); mxfProgram.mxfKeywords.Add(new MxfKeyword { Index = premiere.Index, Word = "Premieres" }); if (mxfProgram.IsSeasonPremiere) { mxfProgram.mxfKeywords.Add(premiere.GetKeyword("Season Premiere")); } if (mxfProgram.IsSeriesPremiere) { mxfProgram.mxfKeywords.Add(premiere.GetKeyword("Series Premiere")); } } else if (mxfProgram.extras["premiere"]) { if (group == keygroups.MOVIES) { mxfProgram.mxfKeywords.Add(mxfKeyGroup.GetKeyword("Premiere")); } else if (Helper.TableContains(sdProgram.Genres, "miniseries")) { var premiere = SdMxf.GetKeywordGroup(Groups[(int)keygroups.PREMIERES]); mxfProgram.mxfKeywords.Add(new MxfKeyword { Index = premiere.Index, Word = "Premieres" }); mxfProgram.mxfKeywords.Add(premiere.GetKeyword("Miniseries Premiere")); } } // now add the real categories if (sdProgram.Genres != null) { foreach (var genre in sdProgram.Genres) { mxfProgram.mxfKeywords.Add(mxfKeyGroup.GetKeyword(genre)); } } // ensure there is at least 1 category to present in category search if (mxfProgram.mxfKeywords.Count > 1) { return; } mxfProgram.mxfKeywords.Add(mxfKeyGroup.GetKeyword("Uncategorized")); }
private static bool BuildLineupServices() { // query what lineups client is subscribed to var clientLineups = SdApi.GetSubscribedLineups(); if (clientLineups == null) { return(false); } // determine if there are custom lineups to consider if (File.Exists(Helper.Epg123CustomLineupsXmlPath)) { CustomLineups customLineups; using (var stream = new StreamReader(Helper.Epg123CustomLineupsXmlPath, Encoding.Default)) { var serializer = new XmlSerializer(typeof(CustomLineups)); TextReader reader = new StringReader(stream.ReadToEnd()); customLineups = (CustomLineups)serializer.Deserialize(reader); reader.Close(); } foreach (var lineup in customLineups.CustomLineup.Where(lineup => config.IncludedLineup.Contains(lineup.Lineup))) { customLineup = lineup; clientLineups.Lineups.Add(new SubscribedLineup { Lineup = lineup.Lineup, Name = lineup.Name, Transport = "CUSTOM", Location = lineup.Location, Uri = "CUSTOM", IsDeleted = false }); customMap = new StationChannelMap { Map = new List <LineupChannel>(), Stations = new List <LineupStation>(), Metadata = new LineupMetadata { Lineup = lineup.Lineup } }; } } // reset counters processedObjects = 0; totalObjects = clientLineups.Lineups.Count; ReportProgress(); // process lineups Logger.WriteMessage($"Entering BuildLineupServices() for {clientLineups.Lineups.Count} lineups."); foreach (var clientLineup in clientLineups.Lineups) { var flagCustom = !string.IsNullOrEmpty(clientLineup.Uri) && clientLineup.Uri.Equals("CUSTOM"); ++processedObjects; ReportProgress(); // request the lineup's station maps StationChannelMap lineupMap = null; if (!flagCustom) { lineupMap = SdApi.GetStationChannelMap(clientLineup.Lineup); if (lineupMap == null) { continue; } foreach (var station in lineupMap.Stations.Where(station => !AllStations.ContainsKey(station.StationId))) { AllStations.Add(station.StationId, station); } } if (!config.IncludedLineup.Contains(clientLineup.Lineup)) { Logger.WriteVerbose($"Subscribed lineup {clientLineup.Lineup} has been EXCLUDED from download and processing."); continue; } if (clientLineup.IsDeleted) { Logger.WriteWarning($"Subscribed lineup {clientLineup.Lineup} has been DELETED at the headend."); continue; } if (flagCustom) { foreach (var station in customLineup.Station.Where(station => station.StationId != null)) { if (AllStations.TryGetValue(station.StationId, out var lineupStation)) { customMap.Map.Add(new LineupChannel { StationId = station.StationId, AtscMajor = station.Number, AtscMinor = station.Subnumber, MatchName = station.MatchName }); CustomStations.Add(station.StationId); customMap.Stations.Add(lineupStation); } else if (!string.IsNullOrEmpty(station.Alternate) && AllStations.TryGetValue(station.Alternate, out lineupStation)) { customMap.Map.Add(new LineupChannel { StationId = station.Alternate, AtscMajor = station.Number, AtscMinor = station.Subnumber, MatchName = station.MatchName }); CustomStations.Add(station.Alternate); customMap.Stations.Add(lineupStation); } } lineupMap = customMap; Logger.WriteVerbose($"Successfully retrieved the station mapping for lineup {clientLineup.Lineup}."); } if (lineupMap == null) { return(false); } var lineupIndex = SdMxf.With.Lineups.Count; SdMxf.With.Lineups.Add(new MxfLineup { Index = lineupIndex + 1, LineupId = clientLineup.Lineup, Name = $"EPG123 {clientLineup.Name} ({clientLineup.Location})" }); // use hashset to make sure we don't duplicate channel entries for this station var channelNumbers = new HashSet <string>(); // build the services and lineup foreach (var station in lineupMap.Stations) { // check if station should be downloaded and processed if (!flagCustom) { if (station == null || (ExcludedStations.Contains(station.StationId) && !CustomStations.Contains(station.StationId))) { continue; } if (!IncludedStations.Contains(station.StationId) && !config.AutoAddNew) { Logger.WriteWarning($"**** Lineup {clientLineup.Name} ({clientLineup.Location}) has added station {station.StationId} ({station.Callsign}). ****"); continue; } } // build the service if necessary var mxfService = SdMxf.GetService(station.StationId); if (string.IsNullOrEmpty(mxfService.CallSign)) { // instantiate stationLogo and override uid StationImage stationLogo = null; mxfService.UidOverride = $"EPG123_{station.StationId}"; // determine station name for ATSC stations var atsc = false; var names = station.Name.Replace("-", "").Split(' '); if (!string.IsNullOrEmpty(station.Affiliate) && names.Length == 2 && names[0] == station.Callsign && $"({names[0]})" == $"{names[1]}") { atsc = true; } // add callsign and station name mxfService.CallSign = CheckCustomCallsign(station.StationId) ?? station.Callsign; mxfService.Name = CheckCustomServicename(station.StationId) ?? (atsc ? $"{station.Callsign} ({station.Affiliate})" : null) ?? station.Name; // add affiliate if available if (!string.IsNullOrEmpty(station.Affiliate)) { mxfService.mxfAffiliate = SdMxf.GetAffiliate(station.Affiliate); } // add station logo if available and allowed var logoPath = $"{Helper.Epg123LogosFolder}\\{station.Callsign}.png"; var urlLogoPath = logoPath.Replace($"{Helper.Epg123LogosFolder}\\", $"http://{Environment.MachineName}:{Helper.TcpPort}/logos/"); var customPath = $"{Helper.Epg123LogosFolder}\\{station.Callsign}_c.png"; if (config.IncludeSdLogos) { // make sure logos directory exists if (!Directory.Exists(Helper.Epg123LogosFolder)) { Directory.CreateDirectory(Helper.Epg123LogosFolder); } if (station.StationLogos != null) { stationLogo = station.StationLogos.FirstOrDefault(arg => arg.Category != null && arg.Category.Equals(config.PreferredLogoStyle, StringComparison.OrdinalIgnoreCase)) ?? station.StationLogos.FirstOrDefault(arg => arg.Category != null && arg.Category.Equals(config.AlternateLogoStyle, StringComparison.OrdinalIgnoreCase)); if (stationLogo != null) { switch (stationLogo.Category) { case "dark": logoPath = logoPath.Replace(".png", "_d.png"); break; case "gray": logoPath = logoPath.Replace(".png", "_g.png"); break; case "light": logoPath = logoPath.Replace(".png", "_l.png"); break; case "white": logoPath = logoPath.Replace(".png", "_w.png"); break; } } } if (stationLogo == null && !config.PreferredLogoStyle.Equals("none", StringComparison.OrdinalIgnoreCase) && !config.AlternateLogoStyle.Equals("none", StringComparison.OrdinalIgnoreCase)) { stationLogo = station.Logo; } // download the logo from SD if not present in the .\logos folder if (stationLogo != null && !File.Exists(logoPath)) { var url = stationLogo.Url; // download, crop & resize logo image, save and add if (!string.IsNullOrEmpty(url)) { StationLogosToDownload.Add(new KeyValuePair <MxfService, string[]>(mxfService, new[] { logoPath, url })); } } // add the existing logo; custom logo overrides downloaded logos if (File.Exists(customPath)) { logoPath = customPath; } urlLogoPath = logoPath.Replace($"{Helper.Epg123LogosFolder}\\", $"http://{Environment.MachineName}:{Helper.TcpPort}/logos/"); if (File.Exists(logoPath)) { mxfService.mxfGuideImage = SdMxf.GetGuideImage(Helper.Standalone ? $"file://{logoPath}" : urlLogoPath, GetStringEncodedImage(logoPath)); } } // handle xmltv logos if (config.XmltvIncludeChannelLogos.Equals("url")) { if (stationLogo != null) { mxfService.extras.Add("logo", stationLogo); } else if (station.Logo?.Url != null) { mxfService.extras.Add("logo", station.Logo); } } else if (config.XmltvIncludeChannelLogos.Equals("local") && config.IncludeSdLogos) { if (File.Exists(logoPath)) { var image = Image.FromFile(logoPath); mxfService.extras.Add("logo", new StationImage { Url = Helper.Standalone ? logoPath : urlLogoPath, Height = image.Height, Width = image.Width }); } else if (stationLogo != null) { mxfService.extras.Add("logo", new StationImage { Url = Helper.Standalone ? logoPath : urlLogoPath }); } } } // match station with mapping for lineup number and subnumbers foreach (var map in lineupMap.Map) { var number = -1; var subnumber = 0; if (!map.StationId.Equals(station.StationId)) { continue; } // QAM if (map.ChannelMajor != 0) { number = map.ChannelMajor; subnumber = map.ChannelMinor; } // ATSC (and CUSTOM) or NTSC else if (map.AtscMajor != 0) { number = map.AtscMajor; subnumber = map.AtscMinor; } else if (map.UhfVhf != 0) { number = map.UhfVhf; } // Cable or Satellite else if (!string.IsNullOrEmpty(map.Channel)) { subnumber = 0; if (Regex.Match(map.Channel, @"[A-Za-z]{1}[\d]{4}").Length > 0) { // 4dtv has channels starting with 2 character satellite identifier number = int.Parse(map.Channel.Substring(2)); } else if (!int.TryParse(Regex.Replace(map.Channel, "[^0-9.]", ""), out number)) { // if channel number is not a whole number, must be a decimal number var numbers = Regex.Replace(map.Channel, "[^0-9.]", "").Replace('_', '.').Replace('-', '.').Split('.'); if (numbers.Length == 2) { number = int.Parse(numbers[0]); subnumber = int.Parse(numbers[1]); } } } string matchName = null; switch (clientLineup.Transport) { case "CUSTOM": matchName = map.MatchName; break; case "DVB-S": var m = Regex.Match(lineupMap.Metadata.Lineup, @"\d+\.\d+"); if (m.Success && map.FrequencyHz > 0 && map.NetworkId > 0 && map.TransportId > 0 && map.ServiceId > 0) { while (map.FrequencyHz > 13000) { map.FrequencyHz /= 1000; } matchName = $"DVBS:{m.Value.Replace(".", "")}:{map.FrequencyHz}:{map.NetworkId}:{map.TransportId}:{map.ServiceId}"; } number = -1; subnumber = 0; break; case "DVB-T": if (map.NetworkId > 0 && map.TransportId > 0 && map.ServiceId > 0) { matchName = $"DVBT:{map.NetworkId}:{map.TransportId}:{map.ServiceId}"; } break; case "Antenna": if (map.AtscMajor > 0 && map.AtscMinor > 0) { matchName = $"OC:{map.AtscMajor}:{map.AtscMinor}"; } break; } var channelNumber = $"{number}{(subnumber > 0 ? $".{subnumber}" : "")}"; if (channelNumbers.Add($"{channelNumber}:{station.StationId}")) { SdMxf.With.Lineups[lineupIndex].channels.Add(new MxfChannel { mxfLineup = SdMxf.With.Lineups[lineupIndex], mxfService = mxfService, Number = number, SubNumber = subnumber, MatchName = matchName }); } } } } if (StationLogosToDownload.Count > 0) { StationLogosDownloadComplete = false; Logger.WriteInformation($"Kicking off background worker to download and process {StationLogosToDownload.Count} station logos."); BackgroundDownloader = new System.ComponentModel.BackgroundWorker(); BackgroundDownloader.DoWork += BackgroundDownloader_DoWork; BackgroundDownloader.RunWorkerCompleted += BackgroundDownloader_RunWorkerCompleted; BackgroundDownloader.WorkerSupportsCancellation = true; BackgroundDownloader.RunWorkerAsync(); } if (SdMxf.With.Services.Count > 0) { // report specific stations that are no longer available var missing = (from station in IncludedStations where SdMxf.With.Services.FirstOrDefault(arg => arg.StationId.Equals(station)) == null select config.StationId.Single(arg => arg.StationId.Equals(station)).CallSign).ToList(); if (missing.Count > 0) { MissingStations = missing.Count; Logger.WriteInformation($"Stations no longer available since last configuration save are: {string.Join(", ", missing)}"); } Logger.WriteMessage("Exiting BuildLineupServices(). SUCCESS."); return(true); } Logger.WriteError($"There are 0 stations queued for download from {clientLineups.Lineups.Count} subscribed lineups. Exiting."); Logger.WriteError("Check that lineups are 'INCLUDED' and stations are selected in the EPG123 GUI."); return(false); }