Java tutorial
/* * 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}®ionId={regionId}"; private static final String DETAILS_URI = BASE_URI + "/event/{eventId}?movieHeight=213&tvShowHeight=213®ionId={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); } }