Java tutorial
/* * Copyright (c) 2004-2014 Matthew Altman & Stuart Boston * * This file is part of TheTVDB API. * * TheTVDB API is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * TheTVDB API is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with TheTVDB API. If not, see <http://www.gnu.org/licenses/>. * */ package com.omertron.thetvdbapi.tools; import com.omertron.thetvdbapi.model.Actor; import com.omertron.thetvdbapi.model.Banner; import com.omertron.thetvdbapi.model.BannerListType; import com.omertron.thetvdbapi.model.BannerType; import com.omertron.thetvdbapi.model.BannerUpdate; import com.omertron.thetvdbapi.model.Banners; import com.omertron.thetvdbapi.model.Episode; import com.omertron.thetvdbapi.model.EpisodeUpdate; import com.omertron.thetvdbapi.model.Series; import com.omertron.thetvdbapi.model.SeriesUpdate; import com.omertron.thetvdbapi.model.TVDBUpdates; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.ws.WebServiceException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class TvdbParser { private static final Logger LOG = LoggerFactory.getLogger(TvdbParser.class); private static final String TYPE_BANNER = "banner"; private static final String TYPE_FANART = "fanart"; private static final String TYPE_POSTER = "poster"; private static final String BANNER_PATH = "BannerPath"; private static final String VIGNETTE_PATH = "VignettePath"; private static final String THUMBNAIL_PATH = "ThumbnailPath"; // The anticipated largest episode number private static final int MAX_EPISODE = 24; // Error messages private static final String ERROR_GET_XML = "Failed to get XML document from URL"; private static final String ERROR_RETRIEVE_EPISODE_INFO = "Unable to retrieve episode information from TheTVDb, try again later."; private static final String ERROR_NOT_ALLOWED_IN_PROLOG = "content is not allowed in prolog"; // Constants private static final int ERROR_MSG_EPISODE = 3; private static final int ERROR_MSG_SEASON = 2; private static final int ERROR_MSG_SERIES = 1; private static final int ERROR_MSG_GROUP_COUNT = 3; // Literals private static final String SERIES = "Series"; private static final String TIME = "time"; private static final String EPISODE = "Episode"; private static final String BANNER = "Banner"; private static final String LAST_UPDATED = "lastupdated"; private static final String OVERVIEW = "Overview"; private static final String IMDB_ID = "IMDB_ID"; private static final String FIRST_AIRED = "FirstAired"; private static final String SERIES_NAME = "SeriesName"; private static final String RATING = "Rating"; private static final String LANGUAGE = "Language"; // Hide the constructor protected TvdbParser() { // prevents calls from subclass throw new UnsupportedOperationException(); } /** * Get a list of the actors from the URL * * @param urlString * @param bannerMirror * @return */ public static List<Actor> getActors(String urlString, String bannerMirror) { List<Actor> results = new ArrayList<Actor>(); Actor actor; Document doc; NodeList nlActor; Node nActor; Element eActor; try { doc = DOMHelper.getEventDocFromUrl(urlString); if (doc == null) { return results; } } catch (WebServiceException ex) { LOG.trace(ERROR_GET_XML, ex); return results; } nlActor = doc.getElementsByTagName("Actor"); for (int loop = 0; loop < nlActor.getLength(); loop++) { nActor = nlActor.item(loop); if (nActor.getNodeType() == Node.ELEMENT_NODE) { eActor = (Element) nActor; actor = new Actor(); actor.setId(DOMHelper.getValueFromElement(eActor, "id")); String image = DOMHelper.getValueFromElement(eActor, "Image"); if (!image.isEmpty()) { actor.setImage(bannerMirror + image); } actor.setName(DOMHelper.getValueFromElement(eActor, "Name")); actor.setRole(DOMHelper.getValueFromElement(eActor, "Role")); actor.setSortOrder(DOMHelper.getValueFromElement(eActor, "SortOrder")); results.add(actor); } } Collections.sort(results); return results; } /** * Get all the episodes from the URL * * @param urlString * @param season * @param bannerMirror * @return */ public static List<Episode> getAllEpisodes(String urlString, int season, String bannerMirror) { List<Episode> episodeList = new ArrayList<Episode>(); Episode episode; NodeList nlEpisode; Node nEpisode; Element eEpisode; try { Document doc = DOMHelper.getEventDocFromUrl(urlString); nlEpisode = doc.getElementsByTagName(EPISODE); for (int loop = 0; loop < nlEpisode.getLength(); loop++) { nEpisode = nlEpisode.item(loop); if (nEpisode.getNodeType() == Node.ELEMENT_NODE) { eEpisode = (Element) nEpisode; episode = parseNextEpisode(eEpisode, bannerMirror); if ((episode != null) && (season == -1 || episode.getSeasonNumber() == season)) { // Add the episode only if the season is -1 (all seasons) or matches the season episodeList.add(episode); } } } } catch (WebServiceException ex) { LOG.warn("All Episodes error: " + ex.getMessage(), ex); } return episodeList; } /** * Get a list of banners from the URL * * @param urlString * @param bannerMirror * @return */ public static Banners getBanners(String urlString, String bannerMirror) { Banners banners = new Banners(); Banner banner; NodeList nlBanners; Node nBanner; Element eBanner; try { Document doc = DOMHelper.getEventDocFromUrl(urlString); if (doc != null) { nlBanners = doc.getElementsByTagName(BANNER); for (int loop = 0; loop < nlBanners.getLength(); loop++) { nBanner = nlBanners.item(loop); if (nBanner.getNodeType() == Node.ELEMENT_NODE) { eBanner = (Element) nBanner; banner = parseNextBanner(eBanner, bannerMirror); if (banner != null) { banners.addBanner(banner); } } } } } catch (WebServiceException ex) { LOG.warn("Banners error: " + ex.getMessage(), ex); } return banners; } /** * Get the episode information from the URL * * @param urlString * @param bannerMirror * @return */ public static Episode getEpisode(String urlString, String bannerMirror) { Episode episode = new Episode(); NodeList nlEpisode; Node nEpisode; Element eEpisode; try { Document doc = DOMHelper.getEventDocFromUrl(urlString); if (doc != null) { nlEpisode = doc.getElementsByTagName(EPISODE); for (int loop = 0; loop < nlEpisode.getLength(); loop++) { nEpisode = nlEpisode.item(loop); if (nEpisode.getNodeType() == Node.ELEMENT_NODE) { eEpisode = (Element) nEpisode; episode = parseNextEpisode(eEpisode, bannerMirror); if (episode != null) { // We only need the first episode break; } } } } } catch (WebServiceException ex) { LOG.warn("Series error: " + ex.getMessage(), ex); } return episode; } /** * Get a list of series from the URL * * @param urlString * @param bannerMirror * @return */ public static List<Series> getSeriesList(String urlString, String bannerMirror) { List<Series> seriesList = new ArrayList<Series>(); Series series; NodeList nlSeries; Node nSeries; Element eSeries; Document doc; try { doc = DOMHelper.getEventDocFromUrl(urlString); } catch (WebServiceException ex) { LOG.trace(ERROR_GET_XML, ex); return seriesList; } if (doc != null) { nlSeries = doc.getElementsByTagName(SERIES); for (int loop = 0; loop < nlSeries.getLength(); loop++) { nSeries = nlSeries.item(loop); if (nSeries.getNodeType() == Node.ELEMENT_NODE) { eSeries = (Element) nSeries; series = parseNextSeries(eSeries, bannerMirror); if (series != null) { seriesList.add(series); } } } } return seriesList; } /** * Get a list of updates from the URL * * @param urlString * @return */ public static TVDBUpdates getUpdates(String urlString) { TVDBUpdates updates = new TVDBUpdates(); Document doc; try { doc = DOMHelper.getEventDocFromUrl(urlString); } catch (WebServiceException ex) { LOG.trace(ERROR_GET_XML, ex); return updates; } if (doc != null) { Node root = doc.getChildNodes().item(0); List<SeriesUpdate> seriesUpdates = new ArrayList<SeriesUpdate>(); List<EpisodeUpdate> episodeUpdates = new ArrayList<EpisodeUpdate>(); List<BannerUpdate> bannerUpdates = new ArrayList<BannerUpdate>(); NodeList updateNodes = root.getChildNodes(); Node updateNode; for (int i = 0; i < updateNodes.getLength(); i++) { updateNode = updateNodes.item(i); if (updateNode.getNodeName().equals(SERIES)) { seriesUpdates.add(parseNextSeriesUpdate((Element) updateNode)); } else if (updateNode.getNodeName().equals(EPISODE)) { episodeUpdates.add(parseNextEpisodeUpdate((Element) updateNode)); } else if (updateNode.getNodeName().equals(BANNER)) { bannerUpdates.add(parseNextBannerUpdate((Element) updateNode)); } } updates.setTime(DOMHelper.getValueFromElement((Element) root, TIME)); updates.setSeriesUpdates(seriesUpdates); updates.setEpisodeUpdates(episodeUpdates); updates.setBannerUpdates(bannerUpdates); } return updates; } /** * Parse the error message to return a more user friendly message * * @param errorMessage * @return */ public static String parseErrorMessage(String errorMessage) { StringBuilder response = new StringBuilder(); Pattern pattern = Pattern.compile(".*?/series/(\\d*?)/default/(\\d*?)/(\\d*?)/.*?"); Matcher matcher = pattern.matcher(errorMessage); // See if the error message matches the pattern and therefore we can decode it if (matcher.find() && matcher.groupCount() == ERROR_MSG_GROUP_COUNT) { int seriesId = Integer.parseInt(matcher.group(ERROR_MSG_SERIES)); int seasonId = Integer.parseInt(matcher.group(ERROR_MSG_SEASON)); int episodeId = Integer.parseInt(matcher.group(ERROR_MSG_EPISODE)); response.append("Series Id: ").append(seriesId); response.append(", Season: ").append(seasonId); response.append(", Episode: ").append(episodeId); response.append(": "); if (episodeId == 0) { // We should probably try an scrape season 0/episode 1 response.append("Episode seems to be a misnamed pilot episode."); } else if (episodeId > MAX_EPISODE) { response.append("Episode number seems to be too large."); } else if (seasonId == 0 && episodeId > 1) { response.append("This special episode does not exist."); } else if (errorMessage.toLowerCase().contains(ERROR_NOT_ALLOWED_IN_PROLOG)) { response.append(ERROR_RETRIEVE_EPISODE_INFO); } else { response.append("Unknown episode error: ").append(errorMessage); } } else { // Don't recognise the error format, so just return it if (errorMessage.toLowerCase().contains(ERROR_NOT_ALLOWED_IN_PROLOG)) { response.append(ERROR_RETRIEVE_EPISODE_INFO); } else { response.append("Episode error: ").append(errorMessage); } } return response.toString(); } /** * Create a List from a delimited string * * @param input * @param delim */ private static List<String> parseList(String input, String delim) { List<String> result = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(input, delim); while (st.hasMoreTokens()) { String token = st.nextToken().trim(); if (token.length() > 0) { result.add(token); } } return result; } /** * Parse the banner record from the document * * @param eBanner * @throws Throwable */ private static Banner parseNextBanner(Element eBanner, String bannerMirror) { Banner banner = new Banner(); String artwork; artwork = DOMHelper.getValueFromElement(eBanner, BANNER_PATH); if (!artwork.isEmpty()) { banner.setUrl(bannerMirror + artwork); } artwork = DOMHelper.getValueFromElement(eBanner, VIGNETTE_PATH); if (!artwork.isEmpty()) { banner.setVignette(bannerMirror + artwork); } artwork = DOMHelper.getValueFromElement(eBanner, THUMBNAIL_PATH); if (!artwork.isEmpty()) { banner.setThumb(bannerMirror + artwork); } banner.setId(DOMHelper.getValueFromElement(eBanner, "id")); banner.setBannerType(BannerListType.fromString(DOMHelper.getValueFromElement(eBanner, "BannerType"))); banner.setBannerType2(BannerType.fromString(DOMHelper.getValueFromElement(eBanner, "BannerType2"))); banner.setLanguage(DOMHelper.getValueFromElement(eBanner, LANGUAGE)); banner.setSeason(DOMHelper.getValueFromElement(eBanner, "Season")); banner.setColours(DOMHelper.getValueFromElement(eBanner, "Colors")); banner.setRating(DOMHelper.getValueFromElement(eBanner, RATING)); banner.setRatingCount(DOMHelper.getValueFromElement(eBanner, "RatingCount")); try { banner.setSeriesName(Boolean.parseBoolean(DOMHelper.getValueFromElement(eBanner, SERIES_NAME))); } catch (WebServiceException ex) { LOG.trace("Failed to transform SeriesName to boolean", ex); banner.setSeriesName(false); } return banner; } /** * Parse the document for episode information * * @param doc * @throws Throwable */ private static Episode parseNextEpisode(Element eEpisode, String bannerMirror) { Episode episode = new Episode(); episode.setId(DOMHelper.getValueFromElement(eEpisode, "id")); episode.setCombinedEpisodeNumber(DOMHelper.getValueFromElement(eEpisode, "Combined_episodenumber")); episode.setCombinedSeason(DOMHelper.getValueFromElement(eEpisode, "Combined_season")); episode.setDvdChapter(DOMHelper.getValueFromElement(eEpisode, "DVD_chapter")); episode.setDvdDiscId(DOMHelper.getValueFromElement(eEpisode, "DVD_discid")); episode.setDvdEpisodeNumber(DOMHelper.getValueFromElement(eEpisode, "DVD_episodenumber")); episode.setDvdSeason(DOMHelper.getValueFromElement(eEpisode, "DVD_season")); episode.setDirectors(parseList(DOMHelper.getValueFromElement(eEpisode, "Director"), "|,")); episode.setEpImgFlag(DOMHelper.getValueFromElement(eEpisode, "EpImgFlag")); episode.setEpisodeName(DOMHelper.getValueFromElement(eEpisode, "EpisodeName")); episode.setEpisodeNumber(getEpisodeValue(eEpisode, "EpisodeNumber")); episode.setFirstAired(DOMHelper.getValueFromElement(eEpisode, FIRST_AIRED)); episode.setGuestStars(parseList(DOMHelper.getValueFromElement(eEpisode, "GuestStars"), "|,")); episode.setImdbId(DOMHelper.getValueFromElement(eEpisode, IMDB_ID)); episode.setLanguage(DOMHelper.getValueFromElement(eEpisode, LANGUAGE)); episode.setOverview(DOMHelper.getValueFromElement(eEpisode, OVERVIEW)); episode.setProductionCode(DOMHelper.getValueFromElement(eEpisode, "ProductionCode")); episode.setRating(DOMHelper.getValueFromElement(eEpisode, RATING)); episode.setSeasonNumber(getEpisodeValue(eEpisode, "SeasonNumber")); episode.setWriters(parseList(DOMHelper.getValueFromElement(eEpisode, "Writer"), "|,")); episode.setAbsoluteNumber(DOMHelper.getValueFromElement(eEpisode, "absolute_number")); String filename = DOMHelper.getValueFromElement(eEpisode, "filename"); if (StringUtils.isNotBlank(filename)) { episode.setFilename(bannerMirror + filename); } episode.setLastUpdated(DOMHelper.getValueFromElement(eEpisode, LAST_UPDATED)); episode.setSeasonId(DOMHelper.getValueFromElement(eEpisode, "seasonid")); episode.setSeriesId(DOMHelper.getValueFromElement(eEpisode, "seriesid")); episode.setAirsAfterSeason(getEpisodeValue(eEpisode, "airsafter_season")); episode.setAirsBeforeEpisode(getEpisodeValue(eEpisode, "airsbefore_episode")); episode.setAirsBeforeSeason(getEpisodeValue(eEpisode, "airsbefore_season")); return episode; } /** * Process the "key" from the element into an integer. * * @param eEpisode * @param key * @return the value, 0 if not found or an error. */ private static int getEpisodeValue(Element eEpisode, String key) { int episodeValue; try { String value = DOMHelper.getValueFromElement(eEpisode, key); episodeValue = NumberUtils.toInt(value, 0); } catch (WebServiceException ex) { LOG.trace("Failed to read episode value", ex); episodeValue = 0; } return episodeValue; } /** * Parse the series record from the document * * @param eSeries * @throws Throwable */ private static Series parseNextSeries(Element eSeries, String bannerMirror) { Series series = new Series(); series.setId(DOMHelper.getValueFromElement(eSeries, "id")); series.setActors(parseList(DOMHelper.getValueFromElement(eSeries, "Actors"), "|,")); series.setAirsDayOfWeek(DOMHelper.getValueFromElement(eSeries, "Airs_DayOfWeek")); series.setAirsTime(DOMHelper.getValueFromElement(eSeries, "Airs_Time")); series.setContentRating(DOMHelper.getValueFromElement(eSeries, "ContentRating")); series.setFirstAired(DOMHelper.getValueFromElement(eSeries, FIRST_AIRED)); series.setGenres(parseList(DOMHelper.getValueFromElement(eSeries, "Genre"), "|,")); series.setImdbId(DOMHelper.getValueFromElement(eSeries, IMDB_ID)); series.setLanguage(DOMHelper.getValueFromElement(eSeries, "language")); series.setNetwork(DOMHelper.getValueFromElement(eSeries, "Network")); series.setOverview(DOMHelper.getValueFromElement(eSeries, OVERVIEW)); series.setRating(DOMHelper.getValueFromElement(eSeries, RATING)); series.setRuntime(DOMHelper.getValueFromElement(eSeries, "Runtime")); series.setSeriesId(DOMHelper.getValueFromElement(eSeries, "SeriesID")); series.setSeriesName(DOMHelper.getValueFromElement(eSeries, SERIES_NAME)); series.setStatus(DOMHelper.getValueFromElement(eSeries, "Status")); String artwork = DOMHelper.getValueFromElement(eSeries, TYPE_BANNER); if (!artwork.isEmpty()) { series.setBanner(bannerMirror + artwork); } artwork = DOMHelper.getValueFromElement(eSeries, TYPE_FANART); if (!artwork.isEmpty()) { series.setFanart(bannerMirror + artwork); } artwork = DOMHelper.getValueFromElement(eSeries, TYPE_POSTER); if (!artwork.isEmpty()) { series.setPoster(bannerMirror + artwork); } series.setLastUpdated(DOMHelper.getValueFromElement(eSeries, LAST_UPDATED)); series.setZap2ItId(DOMHelper.getValueFromElement(eSeries, "zap2it_id")); return series; } /** * Parse the series update record from the document * * @param element */ private static SeriesUpdate parseNextSeriesUpdate(Element element) { SeriesUpdate seriesUpdate = new SeriesUpdate(); seriesUpdate.setId(DOMHelper.getValueFromElement(element, "id")); seriesUpdate.setTime(DOMHelper.getValueFromElement(element, TIME)); return seriesUpdate; } /** * Parse the episode update record from the document * * @param element */ private static EpisodeUpdate parseNextEpisodeUpdate(Element element) { EpisodeUpdate episodeUpdate = new EpisodeUpdate(); episodeUpdate.setId(DOMHelper.getValueFromElement(element, "id")); episodeUpdate.setSeries(DOMHelper.getValueFromElement(element, SERIES)); episodeUpdate.setTime(DOMHelper.getValueFromElement(element, TIME)); return episodeUpdate; } /** * Parse the banner update record from the document * * @param element */ private static BannerUpdate parseNextBannerUpdate(Element element) { BannerUpdate bannerUpdate = new BannerUpdate(); bannerUpdate.setSeasonNum(DOMHelper.getValueFromElement(element, "SeasonNum")); bannerUpdate.setSeries(DOMHelper.getValueFromElement(element, SERIES)); bannerUpdate.setFormat(DOMHelper.getValueFromElement(element, "format")); bannerUpdate.setLanguage(DOMHelper.getValueFromElement(element, "language")); bannerUpdate.setPath(DOMHelper.getValueFromElement(element, "path")); bannerUpdate.setTime(DOMHelper.getValueFromElement(element, TIME)); bannerUpdate.setType(DOMHelper.getValueFromElement(element, "type")); return bannerUpdate; } }