au.id.hazelwood.xmltvguidebuilder.grabber.Grabber.java Source code

Java tutorial

Introduction

Here is the source code for au.id.hazelwood.xmltvguidebuilder.grabber.Grabber.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 au.id.hazelwood.xmltvguidebuilder.grabber;

import au.id.hazelwood.xmltvguidebuilder.config.ChannelConfig;
import au.id.hazelwood.xmltvguidebuilder.config.Config;
import au.id.hazelwood.xmltvguidebuilder.model.ChannelDetail;
import au.id.hazelwood.xmltvguidebuilder.model.ChannelListings;
import au.id.hazelwood.xmltvguidebuilder.model.EpisodeDetail;
import au.id.hazelwood.xmltvguidebuilder.model.ProgrammeDetail;

import org.apache.commons.lang.time.StopWatch;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static au.id.hazelwood.xmltvguidebuilder.utils.DateTimeUtils.formatDurationWords;
import static java.util.Arrays.asList;
import static org.apache.commons.lang.StringUtils.leftPad;
import static org.apache.commons.lang.StringUtils.repeat;
import static org.apache.commons.lang.StringUtils.rightPad;

/**
 * @author Ricky Hazelwood
 * @version 1.0
 */
public class Grabber {
    private static final Logger LOGGER = LoggerFactory.getLogger(Grabber.class);
    private static final String BASE_URI = "https://www.foxtel.com.au/webepg/ws/foxtel";
    private static final String CHANNELS_URI = BASE_URI + "/channels?regionId={regionId}";
    private static final String EVENTS_URI = BASE_URI
            + "/channel/{channelTag}/events?movieHeight=110&tvShowHeight=90&startDate={startDate}&endDate={endDate}&regionId={regionId}";
    private static final String DETAILS_URI = BASE_URI
            + "/event/{eventId}?movieHeight=213&tvShowHeight=213&regionId={regionId}";
    private final RestTemplate restTemplate;

    public Grabber() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        this.restTemplate = new RestTemplate();
        stopWatch.stop();
        LOGGER.debug("Grabber created in {}", formatDurationWords(stopWatch.getTime()));
    }

    public ChannelListings getListing(Config config, DateTime from, DateTime to,
            List<ChannelConfig> channelConfigs) {
        ChannelListings channelListings = new ChannelListings();
        LOGGER.info(repeat("-", 100));
        LOGGER.debug("Fetching channel details");
        Channels channels = getForObject(CHANNELS_URI, Channels.class, config.getRegionId());
        LOGGER.debug("Fetching listing for channels");
        for (ChannelConfig channelConfig : config.getChannelConfigs()) {
            channelListings.addChannel(new ChannelDetail(channelConfig.getId(), channelConfig.getName()));
            Channel channel = getChannel(channels, channelConfig.getId());
            LOGGER.debug("Searching listing for {}", channelConfig);
            if (channel == null) {
                LOGGER.info("{} [no match]", rightPad(channelConfig.getId() + " - " + channelConfig.getName(), 52));
            } else {
                try {
                    addListing(channelListings, config, channelConfig, channel.getChannelTag(), from, to);
                } catch (Exception e) {
                    LOGGER.info("{} [Error fetching listing]",
                            rightPad(channelConfig.getId() + " - " + channelConfig.getName(), 52));
                }
            }
        }
        LOGGER.info(repeat("-", 100));
        return channelListings;
    }

    private void addListing(ChannelListings channelListings, Config config, ChannelConfig channelConfig,
            String channelTag, DateTime from, DateTime to) {
        StopWatch watch = new StopWatch();
        watch.start();
        List<Event> events = getEvents(config, channelTag, from, to);
        LOGGER.debug("Found {} events between {} and {}", events.size(), from, to);
        for (Event event : events) {
            ProgrammeDetail programmeDetails;
            LOGGER.debug("Processing event id {}", event.getEventId());
            DateTime scheduleDate = new DateTime(event.getScheduledDate());
            if (scheduleDate.plusMinutes(event.getDuration()).isAfterNow()
                    && scheduleDate.isBefore(from.plusDays(config.getSynopsis()))) {
                LOGGER.debug("Fetching detailed synopsis for {}", event.getEventId());
                try {
                    EventDetails eventDetails = getForObject(DETAILS_URI, EventDetails.class, event.getEventId(),
                            config.getRegionId());
                    programmeDetails = createProgrammeDetails(eventDetails.getEvent());
                } catch (Exception e) {
                    LOGGER.debug("Failed to get detailed synopsis. Using basic details instead.");
                    programmeDetails = createProgrammeDetails(event);
                }
            } else {
                programmeDetails = createProgrammeDetails(event);
            }
            channelListings.addProgram(channelConfig.getId(), programmeDetails);
        }
        watch.stop();
        String numberOfPrograms = String.valueOf(channelListings.getNumberOfPrograms(channelConfig.getId()));
        LOGGER.info("{} {} events [took {}]", rightPad(channelConfig.getId() + " - " + channelConfig.getName(), 40),
                leftPad(numberOfPrograms, 4), formatDurationWords(watch.getTime()));
    }

    private List<Event> getEvents(Config config, String channelTag, DateTime from, DateTime to) {
        List<Event> events = new ArrayList<>();
        DateTime subFrom = from;
        while (subFrom.isBefore(to)) {
            DateTime subTo = subFrom.plusDays(1);
            if (subTo.isAfter(to)) {
                subTo = to;
            }
            LOGGER.debug("Fetching events between {} and {}", subFrom, subTo);
            try {
                Events subEvents = getForObject(EVENTS_URI, Events.class, channelTag, subFrom.getMillis(),
                        subTo.getMillis(), config.getRegionId());
                LOGGER.debug("Found {} events between {} and {}", subEvents.getEvents().size(), subFrom, subTo);
                events.addAll(subEvents.getEvents());
            } catch (Exception e) {
                LOGGER.debug("Failed to get events between {} and {}");
            }
            subFrom = subTo;
        }
        return events;
    }

    private Channel getChannel(Channels channels, Integer channelId) {
        Channel result = null;
        for (Channel channel : channels.getChannels()) {
            if (channel.getNumber().equals(channelId)) {
                result = channel;
            }
        }
        return result;
    }

    private <T> T getForObject(String uri, Class<T> responseType, Object... urlVariables) {
        int retries = 0;
        while (true) {
            try {
                return restTemplate.getForObject(uri, responseType, urlVariables);
            } catch (RestClientException e) {
                LOGGER.debug(
                        MessageFormat.format("Request for {0} with {1} failed", uri, Arrays.toString(urlVariables)),
                        e);
                if (retries > 5) {
                    throw e;
                }
            }
            retries++;
        }
    }

    private ProgrammeDetail createProgrammeDetails(Event event) {
        DateTime startDate = new DateTime(event.getScheduledDate());
        Minutes duration = Minutes.minutes(event.getDuration());
        String title = event.getProgramTitle();
        EpisodeDetail episodeDetail = new EpisodeDetail(event.getEpisodeTitle(), "TBA", event.getSeriesNumber(),
                event.getEpisodeNumber());
        return new ProgrammeDetail(startDate, duration, title, episodeDetail, Collections.<String>emptyList(),
                event.getParentalRating());
    }

    private ProgrammeDetail createProgrammeDetails(EventDetail eventDetail) {
        DateTime startDate = new DateTime(eventDetail.getScheduledDate());
        Minutes duration = Minutes.minutes(eventDetail.getDuration());
        String title = eventDetail.getProgramTitle();
        EpisodeDetail episodeDetail = new EpisodeDetail(eventDetail.getEpisodeTitle(),
                eventDetail.getShortSynopsis(), eventDetail.getSeriesNumber(), eventDetail.getEpisodeNumber());
        String rating = eventDetail.getParentalRating();
        return new ProgrammeDetail(startDate, duration, title, episodeDetail,
                asList(eventDetail.getGenre(), eventDetail.getSubGenre()), rating);
    }
}