Example #1
0
        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;
            }
        }
Example #2
0
 private static void InitializeKeywordGroups()
 {
     foreach (var group in Groups)
     {
         SdMxf.GetKeywordGroup(group, "m1");
     }
 }
Example #3
0
        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
            }
        }
Example #4
0
        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
                    }
                }
            }
        }
Example #5
0
        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);
        }
Example #6
0
        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);
            }
        }
Example #7
0
        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);
            }
        }
Example #8
0
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        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.");
                }
            }
        }
Example #11
0
        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"]);
            }
        }
Example #12
0
        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);
        }
Example #13
0
        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);
            }
        }
Example #14
0
        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;
        }
Example #15
0
        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"));
        }
Example #16
0
        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);
        }