net.longfalcon.newsj.TVRageService.java Source code

Java tutorial

Introduction

Here is the source code for net.longfalcon.newsj.TVRageService.java

Source

/*
 * Copyright (c) 2016. Sten Martinez
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package net.longfalcon.newsj;

import net.longfalcon.newsj.fs.FileSystemService;
import net.longfalcon.newsj.fs.model.Directory;
import net.longfalcon.newsj.model.Category;
import net.longfalcon.newsj.model.Release;
import net.longfalcon.newsj.model.TvRage;
import net.longfalcon.newsj.persistence.CategoryDAO;
import net.longfalcon.newsj.persistence.ReleaseDAO;
import net.longfalcon.newsj.persistence.TvRageDAO;
import net.longfalcon.newsj.service.TraktService;
import net.longfalcon.newsj.util.ArrayUtil;
import net.longfalcon.newsj.util.DateUtil;
import net.longfalcon.newsj.util.StreamUtil;
import net.longfalcon.newsj.util.ValidatorUtil;
import net.longfalcon.newsj.ws.trakt.TraktEpisodeResult;
import net.longfalcon.newsj.ws.trakt.TraktIdSet;
import net.longfalcon.newsj.ws.trakt.TraktImage;
import net.longfalcon.newsj.ws.trakt.TraktImages;
import net.longfalcon.newsj.ws.trakt.TraktResult;
import net.longfalcon.newsj.ws.trakt.TraktShowResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * NOTE: TVRAGE is no longer a viable API to use. this whole api uses trakt, but tvrage ids. migrate to trakt/tvdb ids
 * in the future.
 * User: Sten Martinez
 * Date: 10/14/15
 * Time: 2:54 PM
 */
@Service
public class TVRageService {
    private static final Log _log = LogFactory.getLog(TVRageService.class);

    private ReleaseDAO releaseDAO;
    private CategoryDAO categoryDAO;
    private TvRageDAO tvRageDAO;

    private TraktService traktService;
    private FileSystemService fileSystemService;

    @Transactional(propagation = Propagation.SUPPORTS)
    public void processTvReleases(boolean lookupTvRage) {
        List<Category> movieCats = categoryDAO.findByParentId(CategoryService.CAT_PARENT_TV);
        List<Integer> ids = new ArrayList<>();
        for (Category category : movieCats) {
            ids.add(category.getId());
        }
        List<Release> releases = releaseDAO.findReleasesByRageIdAndCategoryId(-1, ids);

        if (!releases.isEmpty()) {
            _log.info("Processing tv for " + releases.size() + " releases");
            if (lookupTvRage) {
                _log.info("Looking up tv info from the web");
            }
        }

        for (Release release : releases) {
            ShowInfo showInfo = parseNameEpSeason(release.getName());
            if (showInfo != null) {
                // update release with season, ep, and airdate info (if available) from releasetitle
                updateEpInfo(showInfo, release);

                // find the trakt ID
                long traktId = 0;
                String cleanName = showInfo.getCleanName();
                try {
                    _log.info("looking up " + cleanName + " in db");
                    traktId = getByTitle(cleanName);
                } catch (Exception e) {
                    _log.error("unable to find tvinfo for " + cleanName + " - " + e.toString(), e);
                }

                if (traktId < 0 && traktId > -2 && lookupTvRage) {
                    // if it doesnt exist locally and lookups are allowed lets try to get it
                    _log.info("didnt find trakt id for \"" + cleanName + "\" in local db, checking web...");

                    traktId = getTraktMatch(showInfo);

                    if (traktId > 0) {
                        updateRageInfo(traktId, showInfo, release);
                    } else {
                        // no match
                        //add to tvrage with rageID = -2 and cleanName title only
                        _log.info("no trakt data found for " + cleanName + " - adding placeholder");
                        TvRage tvRage = new TvRage();
                        tvRage.setRageId(-2);
                        tvRage.setTraktId((long) -2);
                        tvRage.setReleaseTitle(cleanName);
                        tvRage.setCreateDate(new Date());
                        tvRage.setDescription("");
                        tvRageDAO.update(tvRage);
                        release.setRageId(tvRage.getId());
                    }
                } else if (traktId > 0) {
                    if (lookupTvRage) {
                        // update airdate and ep title
                        updateEpisodeInfo(traktId, showInfo.getSeason(), showInfo.getEpisode(), release);
                    }
                    TvRage tvRage = tvRageDAO.findByTvTraktId(traktId);
                    release.setRageId(tvRage.getId());

                }

            } else {
                // not a tv episode, so set rageid to n/a
                release.setRageId((long) -2);
            }

            releaseDAO.updateRelease(release);
        }
    }

    public void rebuildTvInfo(TvRage tvRage) {
        try {
            TraktResult traktResult = getTraktMatch(tvRage.getReleaseTitle());
            if (traktResult != null) {
                TraktShowResult showResult = traktResult.getShowResult();

                _log.info("found tvinfo for " + tvRage.getReleaseTitle());

                tvRage.setTraktId(showResult.getIds().getTrakt());
                tvRage.setRageId(showResult.getIds().getTvrage());
                tvRage.setTvdbId(showResult.getIds().getTvdb());
                tvRage.setReleaseTitle(showResult.getTitle());
                TraktImages traktImages = showResult.getTraktImages();
                try {
                    if (traktImages != null) {
                        TraktImage posterImage = traktImages.getPoster();
                        if (posterImage != null) {
                            String posterUrl = posterImage.getMedium();
                            if (ValidatorUtil.isNotNull(posterUrl)) {
                                Directory tempDir = fileSystemService.getDirectory("/temp");
                                File tempFile = tempDir
                                        .getTempFile("image_" + String.valueOf(System.currentTimeMillis()));
                                FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
                                URL url = new URL(posterUrl);
                                URLConnection urlConnection = url.openConnection();
                                urlConnection.setRequestProperty("User-Agent", "Java");
                                StreamUtil.transferByteArray(urlConnection.getInputStream(), fileOutputStream,
                                        1024);
                                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                                InputStream tempFileInputStream = new FileInputStream(tempFile);
                                StreamUtil.transferByteArray(tempFileInputStream, byteArrayOutputStream, 1024);
                                tvRage.setImgData(byteArrayOutputStream.toByteArray());
                            }
                        }
                    }
                } catch (Exception e) {
                    _log.warn("unable to add poster image for " + tvRage.getReleaseTitle(), e);
                }

                tvRage.setDescription(showResult.getOverview());
                tvRageDAO.update(tvRage);
            }
        } catch (Exception e) {
            _log.error(e);
        }
    }

    // restrict to using trakt id since trakt only allows trakt id or imdb to search by episode
    private void updateEpisodeInfo(long traktShowId, String season, String episode, Release release) {
        try {
            int seasonNumber = 0;
            if (season.startsWith("S") || season.startsWith("s")) {
                seasonNumber = Integer.parseInt(season.substring(1));
            } else {
                seasonNumber = Integer.parseInt(season);
            }
            int episodeNumber = 0;
            if (episode.startsWith("E") || episode.startsWith("e")) {
                episodeNumber = Integer.parseInt(episode.substring(1));
            } else {
                episodeNumber = Integer.parseInt(episode);
            }
            TraktEpisodeResult traktEpisodeResult = null;
            try {
                traktEpisodeResult = traktService.getEpisode(traktShowId, seasonNumber, episodeNumber);
            } catch (Exception e) {
                _log.error("error fetching episode data for " + release.getSearchName() + " - " + e.toString());
                _log.debug("", e);
            }
            if (traktEpisodeResult != null) {
                release.setTvAirDate(traktEpisodeResult.getFirstAired());
                release.setTvTitle(traktEpisodeResult.getTitle());
                release.setSeason(String.valueOf(traktEpisodeResult.getSeason()));
                release.setEpisode(String.valueOf(traktEpisodeResult.getNumber()));
            }
        } catch (NumberFormatException e) {
            _log.warn("Parse error: " + e.toString() + " for release " + release.getSearchName(), e);
        }
    }

    private TvRage updateRageInfo(long traktId, ShowInfo showInfo, Release release) {
        // find existing tvRage info
        String season = showInfo.getSeason();
        String episode = showInfo.getEpisode();

        _log.info("looking for existing tvinfo with trakt id " + traktId);
        TvRage tvRage = tvRageDAO.findByTvTraktId(traktId);

        if (tvRage == null) {
            // try looking by name
            tvRage = getTvInfoByTitle(showInfo.getCleanName());
        }
        if (tvRage == null) {
            tvRage = new TvRage();
            tvRage.setCreateDate(new Date());
        }
        //add
        _log.info("no existing tvinfo with trakt Id " + traktId + ", adding from Trakt");
        TraktResult[] traktResults = new TraktResult[0];
        try {
            traktResults = traktService.searchShowByTraktId(traktId);
        } catch (Exception e) {
            _log.error("error fetching show data for " + release.getSearchName() + " - " + e.toString());
            _log.debug("", e);
        }
        if (traktResults.length > 0) {
            TraktResult firstResult = traktResults[0];
            TraktShowResult showResult = firstResult.getShowResult();

            _log.info("found tvinfo for " + showInfo.getCleanName());

            tvRage.setTraktId(showResult.getIds().getTrakt());
            tvRage.setRageId(showResult.getIds().getTvrage());
            tvRage.setTvdbId(showResult.getIds().getTvdb());
            tvRage.setReleaseTitle(showResult.getTitle());
            // TODO: tvRage.setImgData();

            tvRage.setDescription(showResult.getOverview());
            tvRage.setCountry(showInfo.getCountry());
            tvRageDAO.update(tvRage);
            updateEpisodeInfo(showResult.getIds().getTrakt(), season, episode, release);
            release.setRageId(tvRage.getId());
        } else {
            // trakt id that we found before, didnt work this time. this is highly likely to a connectivity or other
            // issue that does not relate the release.
            _log.warn("no results from Trakt found for trakt id " + traktId);
        }

        releaseDAO.updateRelease(release);
        return tvRage;
    }

    private long getRageMatch(ShowInfo showInfo) {
        String cleanName = showInfo.getCleanName();

        try {
            TraktResult[] traktResults = new TraktResult[0];
            try {
                traktResults = traktService.searchTvShowByName(cleanName.toLowerCase());
            } catch (Exception e) {
                _log.error("error fetching show data for " + cleanName + " - " + e.toString());
                _log.debug("", e);
            }
            if (traktResults.length > 0) {
                TraktResult firstResult = traktResults[0];
                if (firstResult.getScore() > 50) {
                    // probably the best match, we wont bother looking elsewhere.
                    if (firstResult.getShowResult() != null) {
                        _log.info("found +50% match: " + firstResult.getShowResult().getTitle());
                        return getRageIdFromTraktResultsSafe(firstResult);
                    } else {
                        return -2;// error
                    }
                } else if (traktResults.length > 1) {
                    String firstResultName = firstResult.getShowResult().getTitle();
                    String secondResultName = traktResults[1].getShowResult().getTitle();
                    double firstSimilarityScore = StringUtils.getJaroWinklerDistance(cleanName, firstResultName);
                    double secondSimilarityScore = StringUtils.getJaroWinklerDistance(cleanName, secondResultName);
                    if (firstSimilarityScore > secondSimilarityScore) {
                        return getRageIdFromTraktResultsSafe(firstResult);
                    } else {
                        return getRageIdFromTraktResultsSafe(traktResults[1]);
                    }
                }
                return getRageIdFromTraktResultsSafe(firstResult);
            }
            _log.warn("no trakt show found for name " + cleanName);
            return -2; // not found
        } catch (Exception e) {
            _log.error(e.toString(), e);
            return -2; // error
        }
    }

    private long getTraktMatch(ShowInfo showInfo) {
        String cleanName = showInfo.getCleanName();
        _log.info("searching trakt for show " + cleanName);
        try {
            TraktResult[] traktResults = new TraktResult[0];
            try {
                traktResults = traktService.searchTvShowByName(cleanName.toLowerCase());
            } catch (Exception e) {
                _log.error("error fetching show data for " + cleanName + " - " + e.toString());
                _log.debug("", e);
            }
            if (traktResults.length > 0) {
                TraktResult firstResult = traktResults[0];
                if (firstResult.getScore() > 50) {
                    // probably the best match, we wont bother looking elsewhere.
                    if (firstResult.getShowResult() != null) {
                        _log.info("found +50% match: " + firstResult.getShowResult().getTitle());
                        return getTraktIdFromTraktResultsSafe(firstResult);
                    } else {
                        return -2;// error
                    }
                } else if (traktResults.length > 1) {
                    String firstResultName = firstResult.getShowResult().getTitle();
                    String secondResultName = traktResults[1].getShowResult().getTitle();
                    double firstSimilarityScore = StringUtils.getJaroWinklerDistance(cleanName, firstResultName);
                    double secondSimilarityScore = StringUtils.getJaroWinklerDistance(cleanName, secondResultName);
                    if (firstSimilarityScore > secondSimilarityScore) {
                        return getTraktIdFromTraktResultsSafe(firstResult);
                    } else {
                        return getTraktIdFromTraktResultsSafe(traktResults[1]);
                    }
                }
                return getTraktIdFromTraktResultsSafe(firstResult);
            }
            return -1; // not found
        } catch (Exception e) {
            _log.error(e.toString(), e);
            return -2; // error
        }
    }

    private TraktResult getTraktMatch(String showname) {
        _log.info("searching trakt for show " + showname);
        try {
            TraktResult[] traktResults = new TraktResult[0];
            try {
                traktResults = traktService.searchTvShowByName(showname.toLowerCase());
            } catch (Exception e) {
                _log.error("error fetching show data for " + showname + " - " + e.toString());
                _log.debug("", e);
            }
            if (traktResults.length > 0) {
                TraktResult firstResult = traktResults[0];
                if (firstResult.getScore() > 50) {
                    // probably the best match, we wont bother looking elsewhere.
                    if (firstResult.getShowResult() != null) {
                        _log.info("found +50% match: " + firstResult.getShowResult().getTitle());
                        return firstResult;
                    } else {
                        return null;// error
                    }
                } else if (traktResults.length > 1) {
                    String firstResultName = firstResult.getShowResult().getTitle();
                    String secondResultName = traktResults[1].getShowResult().getTitle();
                    double firstSimilarityScore = StringUtils.getJaroWinklerDistance(showname, firstResultName);
                    double secondSimilarityScore = StringUtils.getJaroWinklerDistance(showname, secondResultName);
                    if (firstSimilarityScore > secondSimilarityScore) {
                        return firstResult;
                    } else {
                        return traktResults[1];
                    }
                }
                return firstResult;
            }
            return null; // not found
        } catch (Exception e) {
            _log.error(e.toString(), e);
            return null; // error
        }
    }

    private long getRageIdFromTraktResultsSafe(TraktResult traktResult) {
        try {
            if (traktResult == null) {
                return -2;
            }

            TraktShowResult showResult = traktResult.getShowResult();

            if (showResult == null) {
                return -2;
            }

            TraktIdSet traktIdSet = showResult.getIds();

            if (traktIdSet == null) {
                return -2;
            }

            long rageId = traktIdSet.getTvrage();
            _log.info("found rage id: " + rageId);
            return rageId;
        } catch (NullPointerException e) {
            _log.error(e.toString(), e);
            return -2;
        }
    }

    private long getTraktIdFromTraktResultsSafe(TraktResult traktResult) {
        try {
            if (traktResult == null) {
                return -2;
            }

            TraktShowResult showResult = traktResult.getShowResult();

            if (showResult == null) {
                return -2;
            }

            TraktIdSet traktIdSet = showResult.getIds();

            if (traktIdSet == null) {
                return -2;
            }

            long traktId = traktIdSet.getTrakt();
            _log.info("found trakt id: " + traktId);
            return traktId;
        } catch (NullPointerException e) {
            _log.error(e.toString(), e);
            return -2;
        }
    }

    private long getByTitle(String cleanName) {
        long traktId = -1;
        // check if we already have an entry for this show
        TvRage tvRage = tvRageDAO.findByReleaseTitle(cleanName);
        if (tvRage != null) {
            if (tvRage.getTraktId() != null) {
                traktId = tvRage.getTraktId();
            }
            // TODO: update trakt ID for shows we dont have an id for
        } else {
            cleanName = cleanName.replace(" and ", " & ");
            tvRage = tvRageDAO.findByReleaseTitle(cleanName);
            if (tvRage != null) {
                traktId = tvRage.getTraktId();
            }
        }
        return traktId;
    }

    private TvRage getTvInfoByTitle(String cleanName) {
        // check if we already have an entry for this show
        TvRage tvRage = tvRageDAO.findByReleaseTitle(cleanName);
        if (tvRage != null) {
            return tvRage;
        } else {
            cleanName = cleanName.replace(" and ", " & ");
            tvRage = tvRageDAO.findByReleaseTitle(cleanName);
            if (tvRage != null) {
                return tvRage;
            }
        }
        return null;
    }

    private void updateEpInfo(ShowInfo showInfo, Release release) {
        String airDateString = showInfo.getAirDate();
        Date airDate = null;
        if (ValidatorUtil.isNotNull(airDateString)) {
            DateTime airDateTime = DateUtil.parseAirDate(airDateString);
            if (airDateTime != null) {
                airDate = airDateTime.toDate();
            }
        }

        release.setSeriesFull(showInfo.getSeriesFull());
        release.setSeason(showInfo.getSeason());
        release.setEpisode(showInfo.getEpisode());
        release.setTvAirDate(airDate);
        releaseDAO.updateRelease(release);
    }

    private ShowInfo parseNameEpSeason(String name) {

        ShowInfo showInfo = new ShowInfo();
        //S01E01-E02
        //S01E01-02
        Pattern pattern = Pattern.compile("^(.*?)[\\. ]s(\\d{1,2})\\.?e(\\d{1,3})(?:\\-e?|\\-?e)(\\d{1,3})\\.",
                Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode(matcher.group(3) + "," + matcher.group(4));
        }
        //S01E0102 - lame no delimit numbering, regex would collide if there was ever 1000 ep season
        pattern = Pattern.compile("^(.*?)[\\. ]s(\\d{2})\\.?e(\\d{2})(\\d{2})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode(matcher.group(3) + "," + matcher.group(4));
        }
        //S01E01
        //S01.E01
        pattern = Pattern.compile("^(.*?)[\\. ]s(\\d{1,2})\\.?e(\\d{1,3})\\.?", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode(matcher.group(3));
        }
        //S01
        pattern = Pattern.compile("^(.*?)[\\. ]s(\\d{1,2})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode("all");
        }
        //S01D1
        //S1D1
        pattern = Pattern.compile("^(.*?)[\\. ]s(\\d{1,2})d(\\d{1})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode(matcher.group(3));
        }
        //1x01
        pattern = Pattern.compile("^(.*?)[\\. ](\\d{1,2})x(\\d{1,3})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode(matcher.group(3));
        }
        //2009.01.01
        //2009-01-01
        pattern = Pattern.compile("^(.*?)[\\. ](19|20)(\\d{2})[\\.\\-](\\d{2})[\\.\\-](\\d{2})\\.",
                Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2) + matcher.group(3));
            showInfo.setEpisode(matcher.group(4) + "/" + matcher.group(5));
            showInfo.setAirDate(
                    matcher.group(2) + matcher.group(3) + "-" + matcher.group(4) + "-" + matcher.group(5)); //yy-m-d
        }
        //01.01.2009
        pattern = Pattern.compile("^(.*?)[\\. ](\\d{2}).(\\d{2})\\.(19|20)(\\d{2})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(4) + matcher.group(5));
            showInfo.setEpisode(matcher.group(2) + "/" + matcher.group(3));
            showInfo.setAirDate(
                    matcher.group(4) + matcher.group(5) + "-" + matcher.group(2) + "-" + matcher.group(3)); //yy-m-d
        }
        //01.01.09
        pattern = Pattern.compile("^(.*?)[\\. ](\\d{2}).(\\d{2})\\.(\\d{2})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            String yearString = matcher.group(4);
            int year = Integer.parseInt(yearString);
            showInfo.setSeason((year <= 99 && year > 15) ? "19" + matcher.group(4) : "20" + matcher.group(4));
            showInfo.setEpisode(matcher.group(2) + "/" + matcher.group(3));
            showInfo.setAirDate(showInfo.getSeason() + "-" + matcher.group(2) + "-" + matcher.group(3)); //yy-m-d
        }
        //2009.E01
        pattern = Pattern.compile("^(.*?)[\\. ]20(\\d{2})\\.e(\\d{1,3})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason("20" + matcher.group(2));
            showInfo.setEpisode(matcher.group(3));
        }
        //2009.Part1
        pattern = Pattern.compile("^(.*?)[\\. ]20(\\d{2})\\.Part(\\d{1,2})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason("20" + matcher.group(2));
            showInfo.setEpisode(matcher.group(3));
        }
        //Part1/Pt1
        pattern = Pattern.compile("^(.*?)[\\. ](?:Part|Pt)\\.?(\\d{1,2})\\.", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason("1");
            showInfo.setEpisode(matcher.group(2));
        }
        //e.g. The.Pacific.Pt.VI.HDTV.XviD-XII / Part.IV
        pattern = Pattern.compile("^(.*?)[\\. ](?:Part|Pt)\\.?([ivx]+)", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason("1");
            String epLow = matcher.group(2).toLowerCase();
            int e = 0;
            switch (epLow) {
            case "i":
                e = 1;
                break;
            case "ii":
                e = 2;
                break;
            case "iii":
                e = 3;
                break;
            case "iv":
                e = 4;
                break;
            case "v":
                e = 5;
                break;
            case "vi":
                e = 6;
                break;
            case "vii":
                e = 7;
                break;
            case "viii":
                e = 8;
                break;
            case "ix":
                e = 9;
                break;
            case "x":
                e = 10;
                break;
            case "xi":
                e = 11;
                break;
            case "xii":
                e = 12;
                break;
            case "xiii":
                e = 13;
                break;
            case "xiv":
                e = 14;
                break;
            case "xv":
                e = 15;
                break;
            case "xvi":
                e = 16;
                break;
            case "xvii":
                e = 17;
                break;
            case "xviii":
                e = 18;
                break;
            case "xix":
                e = 19;
                break;
            case "xx":
                e = 20;
                break;
            }
            showInfo.setEpisode(String.valueOf(e));
        }
        //e.g. Band.Of.Brothers.EP06.Bastogne.DVDRiP.XviD-DEiTY
        pattern = Pattern.compile("^(.*?)[\\. ]EP?\\.?(\\d{1,3})", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason("1");
            showInfo.setEpisode(matcher.group(2));
        }
        //Season.1
        pattern = Pattern.compile("^(.*?)[\\. ]Seasons?\\.?(\\d{1,2})", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        if (matcher.find()) {
            showInfo.setName(matcher.group(1));
            showInfo.setSeason(matcher.group(2));
            showInfo.setEpisode("all");
        }

        if (ValidatorUtil.isNotNull(showInfo.name)) {
            //country or origin matching
            pattern = Pattern.compile("[\\._ ](US|UK|AU|NZ|CA|NL|Canada|Australia|America)");
            matcher = pattern.matcher(showInfo.getName());
            if (matcher.find()) {
                String group1 = matcher.group(1).toLowerCase();
                if (group1.equals("canada")) {
                    showInfo.setCountry("CA");
                } else if (group1.equals("australia") || group1.equals("aus")) {
                    showInfo.setCountry("AU");
                } else if (group1.equals("america")) {
                    showInfo.setCountry("US");
                } else {
                    showInfo.setCountry(group1.toUpperCase());
                }
            }

            //clean show name
            showInfo.setCleanName(cleanName(showInfo.getName()));

            //check for dates instead of seasons
            if (showInfo.getSeason().length() == 4) {
                showInfo.setSeriesFull(showInfo.getSeason() + "/" + showInfo.getEpisode());

            } else {
                //get year if present (not for releases with dates as seasons)
                pattern = Pattern.compile("[\\._ ](19|20)(\\d{2})", Pattern.CASE_INSENSITIVE);
                matcher = pattern.matcher(name);
                if (matcher.find()) {
                    showInfo.setYear(matcher.group(1) + matcher.group(2));
                }

                if (ValidatorUtil.isNumeric(showInfo.getSeason())) {
                    int season = Integer.parseInt(showInfo.getSeason());
                    showInfo.setSeason(String.format("S%02d", season));
                }

                if (showInfo.getEpisode().contains(",")) {
                    String[] episodesStrings = showInfo.getEpisode().split(",");
                    for (int i = 0; i < episodesStrings.length; i++) {
                        String episodeString = episodesStrings[i];
                        if (ValidatorUtil.isNumeric(episodeString)) {
                            int episode = Integer.parseInt(episodeString);
                            episodesStrings[i] = String.format("E%02d", episode);
                        }
                        showInfo.setEpisode(ArrayUtil.stringify(episodesStrings, ","));
                    }
                } else {
                    String episodeString = showInfo.getEpisode();
                    if (ValidatorUtil.isNumeric(episodeString)) {
                        int episode = Integer.parseInt(episodeString);
                        showInfo.setEpisode(String.format("E%02d", episode));
                    }
                }
                showInfo.setSeriesFull(showInfo.getSeason() + showInfo.getEpisode());
            }

            return showInfo;
        }

        return null;
    }

    private String cleanName(String name) {
        name = name.replace(".", " ");
        name = name.replace("_", " ");
        name = name.replace("&", "and");
        Pattern pattern = Pattern.compile("^(history|discovery) channel", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(name);
        name = matcher.replaceAll("");

        name = name.replaceAll("(\\\\|:|!|\\\"|#|\\*|'|,|\\(|\\)|\\?)", "");

        pattern = Pattern.compile("\\s{2,}", Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(name);
        name = matcher.replaceAll(" ");
        return name.trim();
    }

    public ReleaseDAO getReleaseDAO() {
        return releaseDAO;
    }

    public void setReleaseDAO(ReleaseDAO releaseDAO) {
        this.releaseDAO = releaseDAO;
    }

    public CategoryDAO getCategoryDAO() {
        return categoryDAO;
    }

    public void setCategoryDAO(CategoryDAO categoryDAO) {
        this.categoryDAO = categoryDAO;
    }

    public TvRageDAO getTvRageDAO() {
        return tvRageDAO;
    }

    public void setTvRageDAO(TvRageDAO tvRageDAO) {
        this.tvRageDAO = tvRageDAO;
    }

    public TraktService getTraktService() {
        return traktService;
    }

    public void setTraktService(TraktService traktService) {
        this.traktService = traktService;
    }

    public FileSystemService getFileSystemService() {
        return fileSystemService;
    }

    public void setFileSystemService(FileSystemService fileSystemService) {
        this.fileSystemService = fileSystemService;
    }

    private class ShowInfo {
        private String name;
        private String season;
        private String episode;
        private String seriesFull;
        private String airDate;
        private String country;
        private String year;
        private String cleanName;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getSeason() {
            return season;
        }

        public void setSeason(String season) {
            this.season = season;
        }

        public String getEpisode() {
            return episode;
        }

        public void setEpisode(String episode) {
            this.episode = episode;
        }

        public String getSeriesFull() {
            return seriesFull;
        }

        public void setSeriesFull(String seriesFull) {
            this.seriesFull = seriesFull;
        }

        public String getAirDate() {
            return airDate;
        }

        public void setAirDate(String airDate) {
            this.airDate = airDate;
        }

        public String getCountry() {
            return country;
        }

        public void setCountry(String country) {
            this.country = country;
        }

        public String getYear() {
            return year;
        }

        public void setYear(String year) {
            this.year = year;
        }

        public String getCleanName() {
            return cleanName;
        }

        public void setCleanName(String cleanName) {
            this.cleanName = cleanName;
        }
    }
}