org.rossjohnson.tvdb.io.TvDbDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.rossjohnson.tvdb.io.TvDbDAO.java

Source

package org.rossjohnson.tvdb.io;

import java.io.InputStream;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.rossjohnson.tvdb.EpisodeInfo;
import org.rossjohnson.tvdb.SeriesInfo;
import org.rossjohnson.tvdb.SeriesMappings;

/**
 * Copyright 2010 Ross Johnson
 *
 * This file is part of TVDB Renamer.
 *
 *  TVDB Renamer 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
 *  (at your option) any later version.
 *
 *  TVDB Renamer 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 TVDB Renamer.  If not, see <http://www.gnu.org/licenses/>.
 */
public class TvDbDAO implements TvDAO {
    private static final Log log = LogFactory.getLog(TvDbDAO.class);
    private DateFormat dateFormat;

    private HttpClient client;
    private SeriesMappings mappings;

    public TvDbDAO(SeriesMappings mappings) {
        this.mappings = mappings;
        client = new DefaultHttpClient();
        dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    }

    @Override
    public EpisodeInfo getEpisodeInfo(String seriesName, String episodeName) throws Exception {
        // todo maybe this method should go in another class
        if (seriesName.equals(mappings.getValue(seriesName))) {
            // if there is no mapping, we have to go through all series, looking for a hit

            List<SeriesInfo> allSeries = getAllSeriesInfo(seriesName);
            Collections.sort(allSeries);

            // go thru sorted list of series matches, starting with most recent 
            // "first aired date", looking for episode name match
            for (SeriesInfo series : allSeries) {
                EpisodeInfo ep = getEpisodeInfo(series, episodeName);
                if (ep != null) {
                    return ep;
                }
            }
            return null;
        } else { // they mapped it to another series name/id, so search directly for that one 
            return getEpisodeInfo(getSeriesInfo(seriesName), episodeName);
        }
    }

    @Override
    public EpisodeInfo getEpisodeInfo(SeriesInfo series, String episodeName) throws Exception {
        String showInfoUrl = "http://www.thetvdb.com/data/series/" + series.getSeriesId() + "/all/";
        log.info("Executing " + showInfoUrl);
        HttpGet get = new HttpGet(showInfoUrl);
        HttpEntity entity = client.execute(get).getEntity();
        InputStream is = entity.getContent();

        try {
            Document doc = new SAXReader().read(is);
            String season = null;
            String epNum = null;
            String description = null;
            Date firstAired = null;
            List<Element> list = doc.selectNodes("/Data/Episode");
            int matches = 0;
            for (Element episode : list) {
                Element epName = (Element) episode.selectSingleNode("EpisodeName");
                if (episodeName.equalsIgnoreCase(epName.getText())) {
                    matches++;
                    season = episode.selectSingleNode("Combined_season").getText();
                    epNum = episode.selectSingleNode("EpisodeNumber").getText();
                    firstAired = dateFormat.parse(episode.selectSingleNode("FirstAired").getText());
                    description = episode.selectSingleNode("Overview").getText();
                }
            }
            if (matches > 1) {
                log.warn("Found " + matches + " matches for episode " + episodeName + " of series "
                        + series.getSeriesName());
            }

            if (matches > 0) {
                // for now, just go with last match found.  Eventually we might want to check first airdate
                EpisodeInfo episode = new EpisodeInfo();
                episode.setSeries(series);
                episode.setEpisodeNumber(Integer.parseInt(epNum));
                episode.setSeasonNumber(Integer.parseInt(season));
                episode.setDescription(description);
                episode.setFirstAirDate(firstAired);
                return episode;
            }
        } finally {
            is.close();
        }
        return null;
    }

    @Override
    public List<SeriesInfo> getAllSeriesInfo(String seriesName) throws Exception {
        String showName = URLEncoder.encode(seriesName, "utf-8");
        String query = "http://www.thetvdb.com/api/GetSeries.php?seriesname=" + showName;
        log.info("Executing " + query);
        HttpGet get = new HttpGet(query);
        HttpEntity entity = client.execute(get).getEntity();
        InputStream is = entity.getContent();
        List<SeriesInfo> allMatches = new ArrayList<SeriesInfo>();

        try {
            Document doc = new SAXReader().read(is);
            SeriesInfo seriesInfo;
            List<Element> seriesList = doc.selectNodes("/Data/Series"); // /seriesid");
            switch (seriesList.size()) {
            case 0:
                // no match
                log.warn("Didn't find a match for series named '" + showName + "'");
                break;
            case 1:
                seriesInfo = new SeriesInfo();
                Element singleSeries = seriesList.get(0);
                seriesInfo.setSeriesName(singleSeries.selectSingleNode("SeriesName").getText());
                seriesInfo.setSeriesId(singleSeries.selectSingleNode("seriesid").getText());
                allMatches.add(seriesInfo);
                break;
            default:
                log.info("Found multiple matches for show '" + showName + "'");
                for (Element series : seriesList) {
                    Node firstAiredNode = series.selectSingleNode("FirstAired");
                    if (firstAiredNode != null) {
                        Date firstAired = dateFormat.parse(firstAiredNode.getText());
                        seriesInfo = new SeriesInfo();
                        seriesInfo.setFirstAired(firstAired);
                        seriesInfo.setSeriesName(series.selectSingleNode("SeriesName").getText());
                        seriesInfo.setSeriesId(series.selectSingleNode("seriesid").getText());
                        log.info("Found match: " + seriesInfo);
                        allMatches.add(seriesInfo);
                    }
                }
            }
        } finally {
            is.close();
        }

        return allMatches;
    }

    @Override
    public SeriesInfo getSeriesInfo(final String seriesName) throws Exception {
        // todo this method should also go into another class
        String mappedSeries = mappings.getValue(seriesName);
        log.info("Mapped " + seriesName + " to " + mappedSeries);
        int seriesId = -1;
        try {
            seriesId = Integer.parseInt(mappedSeries);
        } catch (NumberFormatException nfe) {
        } // it wasn't a series id, rather a name

        // if they provide the series ID here, use direct series lookup, don't use getAllSeriesInfo()
        // we have to lookup the series so that we get the actual TVDB series name, rather than 
        // what we have mapped, so that future TVDB lookups based on this name work.
        if (seriesId != -1) {
            return getSeriesInfo(seriesId);
        }

        List<SeriesInfo> allMatches = getAllSeriesInfo(seriesId == -1 ? mappedSeries : seriesName);
        if (allMatches.size() == 0) {
            return null;
        } else if (allMatches.size() == 1) {
            return allMatches.get(0);
        }

        SeriesInfo retVal;
        // for multiple matches, check for mapped name or id
        if (seriesId != -1) {
            for (SeriesInfo series : allMatches) {
                if (seriesId == Integer.parseInt(series.getSeriesId())) {
                    log.info("Found match using series id mapping: " + seriesName + "=" + seriesId);
                    return series;
                }
            }
        }

        // if no match, use one with latest first airdate 
        retVal = getSeriesWithLatestFirstAirDate(allMatches);

        return retVal;
    }

    public SeriesInfo getSeriesInfo(int seriesId) throws Exception {
        SeriesInfo series = null;
        String query = "http://www.thetvdb.com/data/series/" + seriesId + "/all/";
        log.info("Executing " + query);
        Document doc = new SAXReader().read(client.execute(new HttpGet(query)).getEntity().getContent());
        Node seriesElement = doc.selectSingleNode("/Data/Series");
        if (seriesElement != null) {
            series = new SeriesInfo();
            series.setSeriesId(String.valueOf(seriesId));
            series.setSeriesName(seriesElement.selectSingleNode("SeriesName").getText());
            try {
                Date firstAired = dateFormat.parse(seriesElement.selectSingleNode("FirstAired").getText());
                series.setFirstAired(firstAired);
            } catch (Exception e) {
            }
        }
        return series;
    }

    private SeriesInfo getSeriesWithLatestFirstAirDate(List<SeriesInfo> allSeries) {
        Date newest = null;
        for (SeriesInfo series : allSeries) {
            if (newest == null || series.getFirstAired().after(newest)) {
                newest = series.getFirstAired();
            }
        }
        for (SeriesInfo series : allSeries) {
            if (series.getFirstAired().equals(newest)) {
                log.info("Selecting " + series.getSeriesName() + " with first air date " + series.getFirstAired());
                return series;
            }
        }
        return null;
    }
}