Java tutorial
/* Copyright 2010 Meta Broadcast Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.atlasapi.equiv; import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.not; import static org.atlasapi.equiv.generators.AliasResolvingEquivalenceGenerator.aliasResolvingGenerator; import static org.atlasapi.media.entity.Publisher.AMAZON_UK; import static org.atlasapi.media.entity.Publisher.BBC; import static org.atlasapi.media.entity.Publisher.BBC_MUSIC; import static org.atlasapi.media.entity.Publisher.BBC_REDUX; import static org.atlasapi.media.entity.Publisher.BETTY; import static org.atlasapi.media.entity.Publisher.BT_VOD; import static org.atlasapi.media.entity.Publisher.FACEBOOK; import static org.atlasapi.media.entity.Publisher.ITUNES; import static org.atlasapi.media.entity.Publisher.LOVEFILM; import static org.atlasapi.media.entity.Publisher.NETFLIX; import static org.atlasapi.media.entity.Publisher.PA; import static org.atlasapi.media.entity.Publisher.PREVIEW_NETWORKS; import static org.atlasapi.media.entity.Publisher.RADIO_TIMES; import static org.atlasapi.media.entity.Publisher.RDIO; import static org.atlasapi.media.entity.Publisher.RTE; import static org.atlasapi.media.entity.Publisher.SOUNDCLOUD; import static org.atlasapi.media.entity.Publisher.SPOTIFY; import static org.atlasapi.media.entity.Publisher.TALK_TALK; import static org.atlasapi.media.entity.Publisher.YOUTUBE; import static org.atlasapi.media.entity.Publisher.YOUVIEW; import static org.atlasapi.media.entity.Publisher.YOUVIEW_BT; import static org.atlasapi.media.entity.Publisher.YOUVIEW_BT_STAGE; import static org.atlasapi.media.entity.Publisher.YOUVIEW_STAGE; import static org.atlasapi.media.entity.Publisher.YOUVIEW_SCOTLAND_RADIO; import static org.atlasapi.media.entity.Publisher.YOUVIEW_SCOTLAND_RADIO_STAGE; import java.io.File; import java.util.Set; import org.atlasapi.equiv.generators.BroadcastMatchingItemEquivalenceGenerator; import org.atlasapi.equiv.generators.ContainerCandidatesContainerEquivalenceGenerator; import org.atlasapi.equiv.generators.ContainerCandidatesItemEquivalenceGenerator; import org.atlasapi.equiv.generators.ContainerChildEquivalenceGenerator; import org.atlasapi.equiv.generators.EquivalenceGenerator; import org.atlasapi.equiv.generators.FilmEquivalenceGenerator; import org.atlasapi.equiv.generators.RadioTimesFilmEquivalenceGenerator; import org.atlasapi.equiv.generators.ScalingEquivalenceGenerator; import org.atlasapi.equiv.generators.SongTitleTransform; import org.atlasapi.equiv.generators.TitleSearchGenerator; import org.atlasapi.equiv.handlers.BroadcastingEquivalenceResultHandler; import org.atlasapi.equiv.handlers.EpisodeFilteringEquivalenceResultHandler; import org.atlasapi.equiv.handlers.EpisodeMatchingEquivalenceHandler; import org.atlasapi.equiv.handlers.EquivalenceResultHandler; import org.atlasapi.equiv.handlers.EquivalenceSummaryWritingHandler; import org.atlasapi.equiv.handlers.LookupWritingEquivalenceHandler; import org.atlasapi.equiv.handlers.MessageQueueingResultHandler; import org.atlasapi.equiv.handlers.ResultWritingEquivalenceHandler; import org.atlasapi.equiv.results.combining.NullScoreAwareAveragingCombiner; import org.atlasapi.equiv.results.combining.RequiredScoreFilteringCombiner; import org.atlasapi.equiv.results.extractors.MusicEquivalenceExtractor; import org.atlasapi.equiv.results.extractors.PercentThresholdEquivalenceExtractor; import org.atlasapi.equiv.results.filters.AlwaysTrueFilter; import org.atlasapi.equiv.results.filters.ConjunctiveFilter; import org.atlasapi.equiv.results.filters.ContainerHierarchyFilter; import org.atlasapi.equiv.results.filters.EquivalenceFilter; import org.atlasapi.equiv.results.filters.ExclusionListFilter; import org.atlasapi.equiv.results.filters.MediaTypeFilter; import org.atlasapi.equiv.results.filters.MinimumScoreFilter; import org.atlasapi.equiv.results.filters.PublisherFilter; import org.atlasapi.equiv.results.filters.SpecializationFilter; import org.atlasapi.equiv.results.persistence.FileEquivalenceResultStore; import org.atlasapi.equiv.results.persistence.RecentEquivalenceResultStore; import org.atlasapi.equiv.results.scores.Score; import org.atlasapi.equiv.scorers.BroadcastAliasScorer; import org.atlasapi.equiv.scorers.ContainerHierarchyMatchingScorer; import org.atlasapi.equiv.scorers.CrewMemberScorer; import org.atlasapi.equiv.scorers.EquivalenceScorer; import org.atlasapi.equiv.scorers.SequenceContainerScorer; import org.atlasapi.equiv.scorers.SequenceItemScorer; import org.atlasapi.equiv.scorers.SeriesSequenceItemScorer; import org.atlasapi.equiv.scorers.SongCrewMemberExtractor; import org.atlasapi.equiv.scorers.TitleMatchingContainerScorer; import org.atlasapi.equiv.scorers.TitleMatchingItemScorer; import org.atlasapi.equiv.scorers.TitleSubsetBroadcastItemScorer; import org.atlasapi.equiv.update.ContentEquivalenceUpdater; import org.atlasapi.equiv.update.EquivalenceUpdater; import org.atlasapi.equiv.update.EquivalenceUpdaters; import org.atlasapi.equiv.update.NullEquivalenceUpdater; import org.atlasapi.equiv.update.SourceSpecificEquivalenceUpdater; import org.atlasapi.media.channel.ChannelResolver; import org.atlasapi.media.entity.Broadcast; import org.atlasapi.media.entity.Container; import org.atlasapi.media.entity.Content; import org.atlasapi.media.entity.Item; import org.atlasapi.media.entity.Publisher; import org.atlasapi.media.entity.Song; import org.atlasapi.messaging.v3.ContentEquivalenceAssertionMessage; import org.atlasapi.messaging.v3.JacksonMessageSerializer; import org.atlasapi.messaging.v3.KafkaMessagingModule; import org.atlasapi.persistence.content.ContentResolver; import org.atlasapi.persistence.content.ScheduleResolver; import org.atlasapi.persistence.content.SearchResolver; import org.atlasapi.persistence.lookup.LookupWriter; import org.joda.time.DateTime; import org.joda.time.Duration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.metabroadcast.common.collect.MoreSets; import com.metabroadcast.common.queue.MessageSender; import com.metabroadcast.common.time.DateTimeZones; @Configuration @Import({ KafkaMessagingModule.class }) public class EquivModule { private @Value("${equiv.results.directory}") String equivResultsDirectory; private @Value("${messaging.destination.equiv.assert}") String equivAssertDest; private @Value("${equiv.excludedUris}") String excludedUris; private @Autowired ScheduleResolver scheduleResolver; private @Autowired SearchResolver searchResolver; private @Autowired ContentResolver contentResolver; private @Autowired ChannelResolver channelResolver; private @Autowired EquivalenceSummaryStore equivSummaryStore; private @Autowired LookupWriter lookupWriter; private @Autowired KafkaMessagingModule messaging; public @Bean RecentEquivalenceResultStore equivalenceResultStore() { return new RecentEquivalenceResultStore(new FileEquivalenceResultStore(new File(equivResultsDirectory))); } private EquivalenceResultHandler<Container> containerResultHandlers(Iterable<Publisher> publishers) { return new BroadcastingEquivalenceResultHandler<Container>( ImmutableList.of(new LookupWritingEquivalenceHandler<Container>(lookupWriter, publishers), new EpisodeMatchingEquivalenceHandler(contentResolver, equivSummaryStore, lookupWriter, publishers), new ResultWritingEquivalenceHandler<Container>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Container>(equivSummaryStore), new MessageQueueingResultHandler<Container>(equivAssertDestination(), publishers))); } private BroadcastingEquivalenceResultHandler<Item> itemResultHandlers(Set<Publisher> acceptablePublishers) { ImmutableList.Builder<EquivalenceResultHandler<Item>> handlers = ImmutableList.builder(); handlers.add(EpisodeFilteringEquivalenceResultHandler.relaxed( new LookupWritingEquivalenceHandler<Item>(lookupWriter, acceptablePublishers), equivSummaryStore)) .add(new ResultWritingEquivalenceHandler<Item>(equivalenceResultStore())) .add(new EquivalenceSummaryWritingHandler<Item>(equivSummaryStore)); handlers.add(new MessageQueueingResultHandler<Item>(equivAssertDestination(), acceptablePublishers)); return new BroadcastingEquivalenceResultHandler<Item>(handlers.build()); } @Bean protected MessageSender<ContentEquivalenceAssertionMessage> equivAssertDestination() { return messaging.messageSenderFactory().makeMessageSender(equivAssertDest, JacksonMessageSerializer.forType(ContentEquivalenceAssertionMessage.class)); } private static Predicate<Broadcast> YOUVIEW_BROADCAST_FILTER = new Predicate<Broadcast>() { @Override public boolean apply(Broadcast input) { DateTime twoWeeksAgo = new DateTime(DateTimeZones.UTC).minusDays(15); return input.getTransmissionTime().isAfter(twoWeeksAgo); } }; private <T extends Content> EquivalenceFilter<T> standardFilter() { return standardFilter(ImmutableList.<EquivalenceFilter<T>>of()); } private <T extends Content> EquivalenceFilter<T> standardFilter(Iterable<EquivalenceFilter<T>> additional) { return ConjunctiveFilter.valueOf(Iterables.concat(ImmutableList.of(new MinimumScoreFilter<T>(0.2), new MediaTypeFilter<T>(), new SpecializationFilter<T>(), new PublisherFilter<T>(), new ExclusionListFilter<T>(excludedUrisFromProperties())), additional)); } private ImmutableSet<String> excludedUrisFromProperties() { if (Strings.isNullOrEmpty(excludedUris)) { return ImmutableSet.of(); } return ImmutableSet.copyOf(Splitter.on(',').split(excludedUris)); } private ContentEquivalenceUpdater.Builder<Item> standardItemUpdater(Set<Publisher> acceptablePublishers, Set<? extends EquivalenceScorer<Item>> scorers) { return standardItemUpdater(acceptablePublishers, scorers, Predicates.alwaysTrue()); } private ContentEquivalenceUpdater.Builder<Item> standardItemUpdater(Set<Publisher> acceptablePublishers, Set<? extends EquivalenceScorer<Item>> scorers, Predicate<? super Broadcast> filter) { return ContentEquivalenceUpdater.<Item>builder() .withGenerators(ImmutableSet.<EquivalenceGenerator<Item>>of( new BroadcastMatchingItemEquivalenceGenerator(scheduleResolver, channelResolver, acceptablePublishers, Duration.standardMinutes(10), filter))) .withScorers(scorers).withCombiner(new NullScoreAwareAveragingCombiner<Item>()) .withFilter(this.<Item>standardFilter()) .withExtractor( PercentThresholdEquivalenceExtractor.<Item>moreThanPercent(90)) .withHandler((EquivalenceResultHandler<Item>) new BroadcastingEquivalenceResultHandler<Item>( ImmutableList.of( EpisodeFilteringEquivalenceResultHandler .relaxed(new LookupWritingEquivalenceHandler<Item>(lookupWriter, acceptablePublishers), equivSummaryStore), new ResultWritingEquivalenceHandler<Item>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Item>(equivSummaryStore), new MessageQueueingResultHandler<Item>(equivAssertDestination(), acceptablePublishers)))); } private EquivalenceUpdater<Container> topLevelContainerUpdater(Set<Publisher> publishers) { return ContentEquivalenceUpdater.<Container>builder() .withGenerators(ImmutableSet.of( TitleSearchGenerator.create(searchResolver, Container.class, publishers), ScalingEquivalenceGenerator.scale( new ContainerChildEquivalenceGenerator(contentResolver, equivSummaryStore), 20))) .withScorers(ImmutableSet.<EquivalenceScorer<Container>>of(new TitleMatchingContainerScorer())) .withCombiner(new RequiredScoreFilteringCombiner<Container>( new NullScoreAwareAveragingCombiner<Container>(), ContainerChildEquivalenceGenerator.NAME)) .withFilter(this.<Container>standardFilter()) .withExtractor(PercentThresholdEquivalenceExtractor.<Container>moreThanPercent(90)) .withHandler(containerResultHandlers(publishers)).build(); } @Bean public EquivalenceUpdater<Content> contentUpdater() { Set<Publisher> musicPublishers = ImmutableSet.of(BBC_MUSIC, YOUTUBE, SPOTIFY, SOUNDCLOUD, RDIO, AMAZON_UK); Set<Publisher> roviPublishers = ImmutableSet .copyOf(Sets.filter(Publisher.all(), new Predicate<Publisher>() { @Override public boolean apply(Publisher input) { return input.key().endsWith("rovicorp.com"); } })); //Generally acceptable publishers. ImmutableSet<Publisher> acceptablePublishers = ImmutableSet.copyOf(Sets.difference(Publisher.all(), Sets.union( ImmutableSet.of(PREVIEW_NETWORKS, BBC_REDUX, RADIO_TIMES, LOVEFILM, NETFLIX, YOUVIEW, YOUVIEW_STAGE, YOUVIEW_BT, YOUVIEW_BT_STAGE), Sets.union(musicPublishers, roviPublishers)))); EquivalenceUpdater<Item> standardItemUpdater = standardItemUpdater( MoreSets.add(acceptablePublishers, LOVEFILM), ImmutableSet.of(new TitleMatchingItemScorer(), new SequenceItemScorer())).build(); EquivalenceUpdater<Container> topLevelContainerUpdater = topLevelContainerUpdater( MoreSets.add(acceptablePublishers, LOVEFILM)); Set<Publisher> nonStandardPublishers = ImmutableSet.copyOf(Sets.union( ImmutableSet.of(ITUNES, BBC_REDUX, RADIO_TIMES, FACEBOOK, LOVEFILM, NETFLIX, RTE, YOUVIEW, YOUVIEW_STAGE, YOUVIEW_BT, YOUVIEW_BT_STAGE, TALK_TALK, PA, BT_VOD, BETTY), Sets.union(musicPublishers, roviPublishers))); final EquivalenceUpdaters updaters = new EquivalenceUpdaters(); for (Publisher publisher : Iterables.filter(Publisher.all(), not(in(nonStandardPublishers)))) { updaters.register(publisher, SourceSpecificEquivalenceUpdater.builder(publisher).withItemUpdater(standardItemUpdater) .withTopLevelContainerUpdater(topLevelContainerUpdater) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); } updaters.register(RADIO_TIMES, SourceSpecificEquivalenceUpdater.builder(RADIO_TIMES).withItemUpdater(rtItemEquivalenceUpdater()) .withTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); registerYouViewUpdaterForPublisher(YOUVIEW, Sets.union(Sets.difference(acceptablePublishers, ImmutableSet.of(YOUVIEW_STAGE)), ImmutableSet.of(YOUVIEW)), updaters); registerYouViewUpdaterForPublisher(YOUVIEW_STAGE, Sets.union(Sets.difference(acceptablePublishers, ImmutableSet.of(YOUVIEW)), ImmutableSet.of(YOUVIEW_STAGE)), updaters); registerYouViewUpdaterForPublisher(YOUVIEW_BT, Sets.union(Sets.difference(acceptablePublishers, ImmutableSet.of(YOUVIEW_BT_STAGE)), ImmutableSet.of(YOUVIEW_BT)), updaters); registerYouViewUpdaterForPublisher(YOUVIEW_BT_STAGE, Sets.union(Sets.difference(acceptablePublishers, ImmutableSet.of(YOUVIEW_BT)), ImmutableSet.of(YOUVIEW_BT_STAGE)), updaters); registerYouViewUpdaterForPublisher(YOUVIEW_SCOTLAND_RADIO, Sets.union(Sets.difference(acceptablePublishers, ImmutableSet.of(YOUVIEW_SCOTLAND_RADIO_STAGE)), ImmutableSet.of(YOUVIEW_SCOTLAND_RADIO)), updaters); registerYouViewUpdaterForPublisher(YOUVIEW_SCOTLAND_RADIO_STAGE, Sets.union(Sets.difference(acceptablePublishers, ImmutableSet.of(YOUVIEW_SCOTLAND_RADIO)), ImmutableSet.of(YOUVIEW_SCOTLAND_RADIO_STAGE)), updaters); Set<Publisher> reduxPublishers = Sets.union(acceptablePublishers, ImmutableSet.of(BBC_REDUX)); updaters.register(BBC_REDUX, SourceSpecificEquivalenceUpdater.builder(BBC_REDUX) .withItemUpdater(broadcastItemEquivalenceUpdater(reduxPublishers, Score.nullScore(), Predicates.alwaysTrue())) .withTopLevelContainerUpdater(broadcastItemContainerEquivalenceUpdater(reduxPublishers)) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); updaters.register(BETTY, SourceSpecificEquivalenceUpdater.builder(BETTY) .withItemUpdater(aliasIdentifiedBroadcastItemEquivalenceUpdater(ImmutableSet.of(BETTY, YOUVIEW))) .withTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); Set<Publisher> facebookAcceptablePublishers = Sets.union(acceptablePublishers, ImmutableSet.of(FACEBOOK)); updaters.register(FACEBOOK, SourceSpecificEquivalenceUpdater.builder(FACEBOOK) .withItemUpdater(NullEquivalenceUpdater.<Item>get()) .withTopLevelContainerUpdater(facebookContainerEquivalenceUpdater(facebookAcceptablePublishers)) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); updaters.register(ITUNES, SourceSpecificEquivalenceUpdater.builder(ITUNES) .withItemUpdater(vodItemUpdater(acceptablePublishers).build()) .withTopLevelContainerUpdater(vodContainerUpdater(acceptablePublishers)) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); Set<Publisher> lfPublishers = Sets.union(acceptablePublishers, ImmutableSet.of(LOVEFILM)); updaters.register(LOVEFILM, SourceSpecificEquivalenceUpdater.builder(LOVEFILM) .withItemUpdater(vodItemUpdater(lfPublishers).withScorer(new SeriesSequenceItemScorer()).build()) .withTopLevelContainerUpdater(vodContainerUpdater(lfPublishers)) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); Set<Publisher> netflixPublishers = ImmutableSet.of(BBC, NETFLIX); updaters.register(NETFLIX, SourceSpecificEquivalenceUpdater.builder(NETFLIX) .withItemUpdater(vodItemUpdater(netflixPublishers).build()) .withTopLevelContainerUpdater(vodContainerUpdater(netflixPublishers)) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); Set<Publisher> rtePublishers = ImmutableSet.of(PA); updaters.register(RTE, SourceSpecificEquivalenceUpdater.builder(RTE) .withTopLevelContainerUpdater(vodContainerUpdater(rtePublishers)) .withItemUpdater(NullEquivalenceUpdater.<Item>get()) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); updaters.register(TALK_TALK, SourceSpecificEquivalenceUpdater.builder(TALK_TALK) .withItemUpdater(vodItemUpdater(acceptablePublishers).build()) .withTopLevelContainerUpdater(vodContainerUpdater(acceptablePublishers)) .withNonTopLevelContainerUpdater(vodSeriesUpdater(acceptablePublishers)).build()); Set<Publisher> btVodPublishers = ImmutableSet.of(PA); updaters.register(BT_VOD, SourceSpecificEquivalenceUpdater.builder(BT_VOD) .withItemUpdater(vodItemUpdater(btVodPublishers).withScorer(new SeriesSequenceItemScorer()).build()) .withTopLevelContainerUpdater(vodContainerUpdater(btVodPublishers)) .withNonTopLevelContainerUpdater(vodSeriesUpdater(btVodPublishers)).build()); Set<Publisher> itunesAndMusicPublishers = Sets.union(musicPublishers, ImmutableSet.of(ITUNES)); ContentEquivalenceUpdater<Item> muiscPublisherUpdater = ContentEquivalenceUpdater.<Item>builder() .withGenerator(new TitleSearchGenerator<Item>(searchResolver, Song.class, itunesAndMusicPublishers, new SongTitleTransform(), 100)) .withScorer(new CrewMemberScorer(new SongCrewMemberExtractor())) .withCombiner(new NullScoreAwareAveragingCombiner<Item>()).withFilter(AlwaysTrueFilter.<Item>get()) .withExtractor(new MusicEquivalenceExtractor()) .withHandler(new BroadcastingEquivalenceResultHandler<Item>(ImmutableList.of( EpisodeFilteringEquivalenceResultHandler.relaxed( new LookupWritingEquivalenceHandler<Item>(lookupWriter, itunesAndMusicPublishers), equivSummaryStore), new ResultWritingEquivalenceHandler<Item>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Item>(equivSummaryStore)))) .build(); for (Publisher publisher : musicPublishers) { updaters.register(publisher, SourceSpecificEquivalenceUpdater.builder(publisher).withItemUpdater(muiscPublisherUpdater) .withTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); } ImmutableSet<Publisher> roviMatchPublishers = ImmutableSet.of(Publisher.BBC, Publisher.PA, Publisher.YOUVIEW, Publisher.BBC_NITRO, Publisher.BBC_REDUX, Publisher.ITV, Publisher.C4, Publisher.C4_PMLSD, Publisher.C4_PMLSD_P06, Publisher.FIVE); updaters.register(Publisher.ROVI_EN_GB, roviUpdater(Publisher.ROVI_EN_GB, roviMatchPublishers)); updaters.register(Publisher.ROVI_EN_US, roviUpdater(Publisher.ROVI_EN_US, roviMatchPublishers)); return updaters; } private void registerYouViewUpdaterForPublisher(Publisher publisher, Set<Publisher> matchTo, EquivalenceUpdaters updaters) { updaters.register(publisher, SourceSpecificEquivalenceUpdater.builder(publisher) .withItemUpdater( broadcastItemEquivalenceUpdater(matchTo, Score.negativeOne(), YOUVIEW_BROADCAST_FILTER)) .withTopLevelContainerUpdater(broadcastItemContainerEquivalenceUpdater(matchTo)) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()).build()); } private ContentEquivalenceUpdater<Container> vodSeriesUpdater(Set<Publisher> acceptablePublishers) { return ContentEquivalenceUpdater.<Container>builder() .withGenerator( new ContainerCandidatesContainerEquivalenceGenerator(contentResolver, equivSummaryStore)) .withScorer(new SequenceContainerScorer()) .withCombiner(new NullScoreAwareAveragingCombiner<Container>()) .withFilter(this.<Container>standardFilter( ImmutableList.<EquivalenceFilter<Container>>of(new ContainerHierarchyFilter()))) .withExtractor(PercentThresholdEquivalenceExtractor.<Container>moreThanPercent(90)) .withHandler(new BroadcastingEquivalenceResultHandler<Container>(ImmutableList.of( new LookupWritingEquivalenceHandler<Container>(lookupWriter, acceptablePublishers), new ResultWritingEquivalenceHandler<Container>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Container>(equivSummaryStore)))) .build(); } private SourceSpecificEquivalenceUpdater roviUpdater(Publisher roviSource, ImmutableSet<Publisher> roviMatchPublishers) { SourceSpecificEquivalenceUpdater roviUpdater = SourceSpecificEquivalenceUpdater.builder(roviSource) .withItemUpdater(ContentEquivalenceUpdater.<Item>builder() .withGenerators(ImmutableSet.of( new BroadcastMatchingItemEquivalenceGenerator(scheduleResolver, channelResolver, roviMatchPublishers, Duration.standardMinutes(10)), new ContainerCandidatesItemEquivalenceGenerator(contentResolver, equivSummaryStore), new FilmEquivalenceGenerator(searchResolver, roviMatchPublishers, true))) .withScorers(ImmutableSet.of(new TitleMatchingItemScorer(), new SequenceItemScorer())) .withCombiner(new RequiredScoreFilteringCombiner<Item>( new NullScoreAwareAveragingCombiner<Item>(), TitleMatchingItemScorer.NAME)) .withFilter(this.<Item>standardFilter()) .withExtractor(PercentThresholdEquivalenceExtractor.<Item>moreThanPercent(90)) .withHandler(new BroadcastingEquivalenceResultHandler<Item>(ImmutableList.of( EpisodeFilteringEquivalenceResultHandler .strict(new LookupWritingEquivalenceHandler<Item>(lookupWriter, roviMatchPublishers), equivSummaryStore), new ResultWritingEquivalenceHandler<Item>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Item>(equivSummaryStore)))) .build()) .withNonTopLevelContainerUpdater(NullEquivalenceUpdater.<Container>get()) .withTopLevelContainerUpdater(topLevelContainerUpdater(roviMatchPublishers)).build(); return roviUpdater; } private EquivalenceUpdater<Container> facebookContainerEquivalenceUpdater( Set<Publisher> facebookAcceptablePublishers) { return ContentEquivalenceUpdater.<Container>builder() .withGenerators(ImmutableSet.of( TitleSearchGenerator.create(searchResolver, Container.class, facebookAcceptablePublishers), aliasResolvingGenerator(contentResolver, Container.class))) .withScorers(ImmutableSet.<EquivalenceScorer<Container>>of()) .withCombiner(NullScoreAwareAveragingCombiner.<Container>get()) .withFilter(ConjunctiveFilter.valueOf(ImmutableList.of(new MinimumScoreFilter<Container>(0.2), new SpecializationFilter<Container>()))) .withExtractor(PercentThresholdEquivalenceExtractor.<Container>moreThanPercent(90)) .withHandler(containerResultHandlers(facebookAcceptablePublishers)).build(); } private EquivalenceUpdater<Container> vodContainerUpdater(Set<Publisher> acceptablePublishers) { return ContentEquivalenceUpdater.<Container>builder() .withGenerator(TitleSearchGenerator.create(searchResolver, Container.class, acceptablePublishers)) .withScorers(ImmutableSet.of(new TitleMatchingContainerScorer(), new ContainerHierarchyMatchingScorer(contentResolver))) .withCombiner(new RequiredScoreFilteringCombiner<Container>( new NullScoreAwareAveragingCombiner<Container>(), TitleMatchingContainerScorer.NAME)) .withFilter(this.<Container>standardFilter()) .withExtractor(PercentThresholdEquivalenceExtractor.<Container>moreThanPercent(90)) .withHandler(containerResultHandlers(acceptablePublishers)).build(); } private ContentEquivalenceUpdater.Builder<Item> vodItemUpdater(Set<Publisher> acceptablePublishers) { return ContentEquivalenceUpdater.<Item>builder() .withGenerators(ImmutableSet.of( new ContainerCandidatesItemEquivalenceGenerator(contentResolver, equivSummaryStore), new FilmEquivalenceGenerator(searchResolver, acceptablePublishers, true))) .withScorers(ImmutableSet.of(new TitleMatchingItemScorer(), new SequenceItemScorer())) .withCombiner(new RequiredScoreFilteringCombiner<Item>(new NullScoreAwareAveragingCombiner<Item>(), TitleMatchingItemScorer.NAME)) .withFilter(this.<Item>standardFilter()) .withExtractor(PercentThresholdEquivalenceExtractor.<Item>moreThanPercent(90)) .withHandler(new BroadcastingEquivalenceResultHandler<Item>(ImmutableList.of( EpisodeFilteringEquivalenceResultHandler.strict( new LookupWritingEquivalenceHandler<Item>(lookupWriter, acceptablePublishers), equivSummaryStore), new ResultWritingEquivalenceHandler<Item>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Item>(equivSummaryStore)))); } private EquivalenceUpdater<Container> broadcastItemContainerEquivalenceUpdater(Set<Publisher> sources) { return ContentEquivalenceUpdater.<Container>builder() .withGenerators( ImmutableSet.of(TitleSearchGenerator.create(searchResolver, Container.class, sources), new ContainerChildEquivalenceGenerator(contentResolver, equivSummaryStore))) .withScorers(ImmutableSet.of(new TitleMatchingContainerScorer())) .withCombiner(new RequiredScoreFilteringCombiner<Container>( new NullScoreAwareAveragingCombiner<Container>(), ContainerChildEquivalenceGenerator.NAME)) .withFilter(this.<Container>standardFilter()) .withExtractor(PercentThresholdEquivalenceExtractor.<Container>moreThanPercent(90)) .withHandler(containerResultHandlers(sources)).build(); } private EquivalenceUpdater<Item> broadcastItemEquivalenceUpdater(Set<Publisher> sources, Score titleMismatch, Predicate<? super Broadcast> filter) { return standardItemUpdater(sources, ImmutableSet.of(new TitleMatchingItemScorer(), new SequenceItemScorer(), new TitleSubsetBroadcastItemScorer(contentResolver, titleMismatch, 80/*percent*/)), filter).build(); } private EquivalenceUpdater<Item> aliasIdentifiedBroadcastItemEquivalenceUpdater(Set<Publisher> sources) { return ContentEquivalenceUpdater.<Item>builder() .withGenerator(new BroadcastMatchingItemEquivalenceGenerator(scheduleResolver, channelResolver, sources, Duration.standardMinutes(5), Predicates.alwaysTrue())) .withScorer(new BroadcastAliasScorer(Score.negativeOne())) .withCombiner(new NullScoreAwareAveragingCombiner<Item>()).withFilter(AlwaysTrueFilter.<Item>get()) .withExtractor(new PercentThresholdEquivalenceExtractor<Item>(0.95)) .withHandler(itemResultHandlers(sources)).build(); } private EquivalenceUpdater<Item> rtItemEquivalenceUpdater() { return ContentEquivalenceUpdater.<Item>builder() .withGenerators(ImmutableSet.of(new RadioTimesFilmEquivalenceGenerator(contentResolver), new FilmEquivalenceGenerator(searchResolver, ImmutableSet.of(Publisher.PREVIEW_NETWORKS), false))) .withScorers(ImmutableSet.<EquivalenceScorer<Item>>of()) .withCombiner(new NullScoreAwareAveragingCombiner<Item>()).withFilter( this.<Item>standardFilter()) .withExtractor(PercentThresholdEquivalenceExtractor.<Item>moreThanPercent(90)) .withHandler( new BroadcastingEquivalenceResultHandler<Item>(ImmutableList.of( EpisodeFilteringEquivalenceResultHandler.relaxed( new LookupWritingEquivalenceHandler<Item>(lookupWriter, ImmutableSet.of(RADIO_TIMES, PA, PREVIEW_NETWORKS)), equivSummaryStore), new ResultWritingEquivalenceHandler<Item>(equivalenceResultStore()), new EquivalenceSummaryWritingHandler<Item>(equivSummaryStore)))) .build(); } }