Пример #1
0
        /// <summary>
        /// Takes Bleb channel names and populates the TvChannels table
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="blebChannelNames"></param>
        /// <param name="days"></param>
        /// <param name="channelHandler"></param>
        /// <param name="handler"></param>
        /// <returns></returns>
        public void ImportChannels(TvProgramBroadCastListingImport tag, IEnumerable<string> blebChannelNames, AddTvChannelSourceInfoHandler channelHandler)
        {
            var importer = new BlebXmltvProvider();

            var channelNames = blebChannelNames.ToArray();

            int[] days = new[] { 0 };
            var results = importer.GetFeedResults(blebChannelNames, days).Result;

            if (results.channel.Length != channelNames.Length) throw new InvalidOperationException("Got wrong number of result channels so can't import pair them up with names");

            // look through the response and create each channel
            for (int i = 0; i < channelNames.Length; i++) {
                var channel = results.channel[i];

                channelHandler(tag, new AddTvChannelInfo {
                    DisplayName = channel.displayname.Trim(),
                    SourceName = channelNames[i],
                    Uri = channel.id
                });
            }
        }
Пример #2
0
        /// <summary>
        /// This method basically does what it can to possibly sanitise the input.
        /// Tries to match a program, extract a series and episode number, extract attributes like S for subtitles etc.
        /// It should be specific to each feed probably. The regexes are quite specific thouogh.
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="handler"></param>
        /// <param name="channelUriLookup"></param>
        /// <param name="results"></param>
        private void AddPrograms(TvProgramBroadCastListingImport tag, AddTvBroadCastListingInfoHandler handler, Dictionary<string, TvChannelListingSource> channelUriLookup, tvProgramme[] results)
        {
            foreach (var program in results) {

                // the xmltv sometimes have missing end times but these are recognised by start == stop
                var start = BlebXmltvProvider.ParseDateStringToUtc(program.start);
                DateTime? end = BlebXmltvProvider.ParseDateStringToUtc(program.stop);

                // we use a null in the DB if we don't know the end time
                if (start == end) {
                    end = null;
                }

                // try and clean up the program titles etc.
                var programTitle = program.title.Value;
                var episodeTitle = program.subtitle?.Value;
                var episodeDescription = program.desc?.Value;

                // and see if we can extract any of this metadata
                int? seriesNumber = null;
                int? episodeNumber = null;
                BroadCastListingAttributes attribs = 0;

                // try and clean up the program titles etc.
                if (episodeDescription != null) {

                    // the bleb import feed titles sometimes run into the descriptions using ...s
                    if (programTitle.EndsWith("...") && episodeDescription.StartsWith("...")) {
                        // find when the runon ends, ignoring the start ...
                        var endOfRunOn = episodeDescription.IndexOf('.', 3);

                        if (endOfRunOn != -1) {
                            // trim out the ...s and append the episode description
                            programTitle = programTitle.Substring(0, programTitle.Length - 3) + ' ' + episodeDescription.Substring(3, endOfRunOn - 3);
                            // then trim off title from the start of the description, and the full stop and any spaces
                            episodeDescription = episodeDescription.Substring(endOfRunOn + 1).Trim(' ');
                        }
                    }

                    // do a bit of basic sanitisation (necessary for the runons above! but shouldn't be harmful here)
                    programTitle = programTitle.Trim(' ', '.');

                    // AlsoInHdRegex
                    try {
                        var match = AlsoInHdRegex.Match(episodeDescription);
                        if (match.Success) {
                            attribs = attribs | BroadCastListingAttributes.AvailableInHD;
                            episodeDescription = match.Groups["pre"].Value + match.Groups["post"].Value;
                        }
                    }
                    catch { }

                    // BBCAttribsRegex
                    try {
                        // then try and pull out BBC style attribs like HD, subtitles, audio descriptions like [S, AD]
                        var match = BBCAttribsRegex.Match(episodeDescription);
                        if (match.Success) {
                            if (match.Groups["attribs"].Success) {
                                var strings = match.Groups["attribs"].Value.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
                                if (strings.Contains("S")) attribs = attribs | BroadCastListingAttributes.Subtitles;
                                if (strings.Contains("AD")) attribs = attribs | BroadCastListingAttributes.AudioDescription;
                            }
                            episodeDescription = match.Groups["pre"].Value + match.Groups["post"].Value;
                        }
                    }
                    catch { }

                    try {

                        // then try and pull out some episode information with the N/N format
                        var match = BBCEpisodeRegex.Match(episodeDescription);
                        if (match.Success) {
                            episodeNumber = int.Parse(match.Groups["episode"].Value);
                            episodeDescription = match.Groups["post"].Value;
                        }
                        else {
                            // and then try SN, EN format
                            match = OtherEpisodeRegex.Match(episodeDescription);
                            if (match.Success) {
                                seriesNumber = int.Parse(match.Groups["series"].Value);
                                episodeNumber = int.Parse(match.Groups["episode"].Value);
                                episodeDescription = match.Groups["pre"].Value + match.Groups["post"].Value;
                            }
                        }

                    }
                    // ignore any metadata that fails extraction but still save the rest
                    catch (ArgumentNullException) { }
                    catch (FormatException) { }

                }

                // and remove any double spaces we might have introduced
                episodeDescription = episodeDescription?.Replace("  ", " ").Trim();

                try {

                    handler(
                        tag,
                        channelUriLookup[program.channel],
                        new AddBroadcastListingInfo {
                            StartAt = start,
                            EndAt = end,
                            ProgramTitle = programTitle,
                            EpisodeTitle = episodeTitle,
                            EpisodeDescription = episodeDescription,
                            SeriesNumber = seriesNumber,
                            EpisodeNumber = episodeNumber,
                            Attributes = (int)attribs
                        }
                    );

                }
                catch (TvBroadCastListingAlreadyExistsException) {
                    // the feed just violated the PK for this entry, carry on with the next one
                    continue;
                }

            }
        }
Пример #3
0
        /// <summary>
        /// Creates a TvBroadcastListing
        /// </summary>
        /// <param name="importTag"></param>
        /// <param name="channelSource"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        public TvProgramBroadCastListing AddBroadcastListing(TvProgramBroadCastListingImport importTag, TvChannelListingSource channelSource, IAddTvBroadCastListingInfo info)
        {
            // check if this import's going to violate the PK by trying to import duplicate Listings (on the same ImportId)
            if (CheckForViolatedPK(importTag, channelSource, info) != null)
                throw new TvBroadCastListingAlreadyExistsException(
                    string.Format(
                        "An item with that PK (import, channel, startat) {{{0}, {1}, {2}}} already exists",
                        importTag.TvProgramBroadCastListingImportId, channelSource.TvChannelId, info.StartAt
                    )
                );

            // clean up any whitespace on the text fields and replace as nulls where possible
            info.ProgramTitle = info.ProgramTitle.Trim();
            info.EpisodeTitle = string.IsNullOrWhiteSpace(info.EpisodeTitle) ? null : info.EpisodeTitle.Trim();
            info.EpisodeDescription = string.IsNullOrWhiteSpace(info.EpisodeDescription) ? null : info.EpisodeDescription.Trim();

            // try and find or generate program information from this listing
            TvProgram program = GetOrCreateTvProgramFromListingInfo(info);

            // try and find or generate episode information from this listing
            TvProgramEpisode episode = GetOrCreateTvProgramEpisodeFromListingInfo(program, info);

            // if we don't have an EndDate we can't create a listing
            if (info.EndAt == null || info.StartAt == info.EndAt) return null;

            // see if there's an existing listing for this channel with these exact times
            var existing = FindExistingListing(channelSource, info.StartAt, info.EndAt.Value);
            if (existing != null) {
                // check if it's the same in every way except the importer
                if(
                    // TvChannelId, StartAt, EndAt, Superceded==false are done by the DB
                    existing.ProgramTitle == info.ProgramTitle
                    && existing.EpisodeTitle == info.EpisodeTitle
                    && existing.EpisodeDescription == info.EpisodeDescription
                    && existing.TvProgramId == program.TvProgramId
                    && existing.TvProgramEpisodeId == episode?.TvProgramEpisodeId
                    && existing.Attributes == info.Attributes
                ) {
                    // already an exact match, return that instead
                    return existing;
                }
            }

            // create and add the new listing
            var newItem = new TvProgramBroadCastListing {
                TvProgramBroadCastListingImportId = importTag.TvProgramBroadCastListingImportId,
                TvChannelId = channelSource.TvChannelId,
                StartAt = info.StartAt,
                EndAt = info.EndAt,
                ProgramTitle = info.ProgramTitle,
                EpisodeTitle = info.EpisodeTitle,
                EpisodeDescription = info.EpisodeDescription,
                TvProgramId = program?.TvProgramId,
                TvProgramEpisodeId = episode?.TvProgramEpisodeId,
                Attributes = info.Attributes,
                Superceded = false
            };
            TvListings.Add(newItem);

            // if we had an existing item that wasn't an exact match we mark it as superceded since we've just added a new one
            if (existing != null) {
                existing.Superceded = true;
                TvListings.Update(existing);
            }

            // return the new item we just made
            return newItem;
        }
Пример #4
0
        /// <summary>
        /// Performs an update for the specified channels for the specified days!
        /// </summary>
        /// <param name="tag"></param>
        /// <param name="channels"></param>
        /// <param name="days"></param>
        /// <param name="handler"></param>
        /// <returns></returns>
        public void ImportPrograms(TvProgramBroadCastListingImport tag, IEnumerable<TvChannelListingSource> channels, IEnumerable<int> days, AddTvBroadCastListingInfoHandler handler)
        {
            var importer = new BlebXmltvProvider();

            // get a list of the source names to pass to the importer
            var blebChannelNames = channels.Select(x => x.SourceChannelName);

            var results = importer.GetFeedResults(blebChannelNames, days).Result;

            // create a dictionary of channel URI -> TvChannel for the importer to find the TvChannelId from the URI
            var channelUriLookup = channels.ToDictionary(x => x.SourceChannelUri, x => x);

            // add each program
            if (results.programme != null) {
                AddPrograms(tag, handler, channelUriLookup, results.programme);
            }
        }
Пример #5
0
        /// <summary>
        /// Checks for an exact PK match on local changes only
        /// </summary>
        /// <param name="importTag"></param>
        /// <param name="channelSource"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        protected TvProgramBroadCastListing CheckForViolatedPK(TvProgramBroadCastListingImport importTag, TvChannelListingSource channelSource, IAddTvBroadCastListingInfo info)
        {
            // in-memory dupe detection to stop duplicate PKs generated by broken feed / import processes
            var local =
                ChangeTracker.Entries<TvProgramBroadCastListing>()
                .SingleOrDefault(x =>
                    x.Property(y => y.TvProgramBroadCastListingImportId).CurrentValue == importTag.TvProgramBroadCastListingImportId
                    && x.Property(y => y.TvChannelId).CurrentValue == channelSource.TvChannelId
                    && x.Property(y => y.StartAt).CurrentValue == info.StartAt
                );
            if (local != null) return local.Entity;

            // we don't need to check the database here because this TvProgramBroadCastListingImportId has been created by this thread
            return null;
        }
Пример #6
0
        public void MarkImportFailed(TvProgramBroadCastListingImport tag, string error)
        {
            var import = Imports.Single(x => x.TvProgramBroadCastListingImportId == tag.TvProgramBroadCastListingImportId);

            import.Running = false;
            import.End = DateTime.UtcNow;
            import.Error = error;

            Imports.Update(import);

            SaveChanges();
        }
Пример #7
0
        public void MarkImportComplete(TvProgramBroadCastListingImport tag, DateTime publishDateUtc)
        {
            tag.Running = false;
            tag.End = DateTime.UtcNow;
            tag.PublishDate = publishDateUtc;

            Imports.Update(tag);

            SaveChanges();
        }
Пример #8
0
        public TvProgramBroadCastListingImport CreateImportTag(TvListingSource source)
        {
            var import = new TvProgramBroadCastListingImport {
                ListingSource = source,
                Start = DateTime.UtcNow,
                Running = true
            };

            Imports.Add(import);
            SaveChanges();

            return import;
        }
Пример #9
0
        /// <summary>
        /// Locates or creates a TvChannel and its corresponding TvChannelListingSource
        /// </summary>
        /// <param name="import"></param>
        /// <param name="info"></param>
        /// <returns></returns>
        public TvChannelListingSource AddTvChannelListingSource(TvProgramBroadCastListingImport import, IAddTvChannelSourceInfo info)
        {
            // try and find a matching channel by uri
            var local = ChangeTracker.Entries<TvChannelListingSource>().SingleOrDefault(x => x.Property(y => y.SourceChannelUri).CurrentValue == info.Uri);
            if (local != null) return local.Entity;

            // try and find a matching channel by uri
            var remote = TvChannelListingSources.SingleOrDefault(x => x.SourceChannelUri == info.Uri);
            if (remote != null) return remote;

            // add a channel with that displayname
            var newChan = new TvChannel {
                ChannelName = info.DisplayName.Trim()
            };
            TvChannels.Add(newChan);

            // and a link between it and the source with the info it needs for future requests
            var newSource = new TvChannelListingSource {
                SourceChannelUri = info.Uri,
                SourceChannelName = info.SourceName
            };
            TvChannelListingSources.Add(newSource);

            return newSource;
        }