diff --git a/source/net/filebot/format/MediaBindingBean.java b/source/net/filebot/format/MediaBindingBean.java index 58827bd3..ac6a9b56 100644 --- a/source/net/filebot/format/MediaBindingBean.java +++ b/source/net/filebot/format/MediaBindingBean.java @@ -50,7 +50,6 @@ import net.filebot.web.MoviePart; import net.filebot.web.MultiEpisode; import net.filebot.web.SearchResult; import net.filebot.web.SimpleDate; -import net.filebot.web.SortOrder; import net.filebot.web.TheTVDBSearchResult; import com.cedarsoftware.util.io.JsonWriter; @@ -556,14 +555,13 @@ public class MediaBindingBean { if (metaInfo == null) { try { if (infoObject instanceof Episode) - metaInfo = WebServices.TheTVDB.getSeriesInfoByName(((Episode) infoObject).getSeriesName(), Locale.ENGLISH); + metaInfo = WebServices.TheTVDB.getSeriesInfoByName(getEpisode().getSeriesName(), getEpisode().getLanguage()); if (infoObject instanceof Movie) - metaInfo = WebServices.TheMovieDB.getMovieInfo(getMovie(), Locale.ENGLISH, true); + metaInfo = WebServices.TheMovieDB.getMovieInfo(getMovie(), getMovie().getLanguage(), true); } catch (Exception e) { throw new RuntimeException("Failed to retrieve metadata: " + infoObject, e); } } - return createMapBindings(new PropertyBindings(metaInfo, null)); } @@ -588,7 +586,7 @@ public class MediaBindingBean { @Define("episodelist") public Object getEpisodeList() throws Exception { - return ((EpisodeListProvider) getDatabase()).getEpisodeList(getSeriesObject(), SortOrder.Airdate, Locale.ENGLISH); + return ((EpisodeListProvider) getDatabase()).getEpisodeList(getSeriesObject(), getEpisode().getOrder(), getEpisode().getLanguage()); } @Define("database") diff --git a/source/net/filebot/web/AnidbClient.java b/source/net/filebot/web/AnidbClient.java index 839346aa..c97f5601 100644 --- a/source/net/filebot/web/AnidbClient.java +++ b/source/net/filebot/web/AnidbClient.java @@ -92,7 +92,7 @@ public class AnidbClient extends AbstractEpisodeListProvider { } @Override - public List fetchEpisodeList(SearchResult searchResult, SortOrder sortOrder, Locale language) throws Exception { + public List fetchEpisodeList(SearchResult searchResult, SortOrder sortOrder, Locale locale) throws Exception { AnidbSearchResult anime = (AnidbSearchResult) searchResult; // e.g. http://api.anidb.net:9001/httpapi?request=anime&client=filebot&clientver=1&protover=1&aid=4521 @@ -112,7 +112,7 @@ public class AnidbClient extends AbstractEpisodeListProvider { // select main title and anime start date SimpleDate seriesStartDate = SimpleDate.parse(selectString("//startdate", dom), "yyyy-MM-dd"); - String animeTitle = selectString("//titles/title[@type='official' and @lang='" + language.getLanguage() + "']", dom); + String animeTitle = selectString("//titles/title[@type='official' and @lang='" + locale.getLanguage() + "']", dom); if (animeTitle.isEmpty()) { animeTitle = selectString("//titles/title[@type='main']", dom); } @@ -126,15 +126,15 @@ public class AnidbClient extends AbstractEpisodeListProvider { if (type == 1 || type == 2) { SimpleDate airdate = SimpleDate.parse(getTextContent("airdate", node), "yyyy-MM-dd"); - String title = selectString(".//title[@lang='" + language.getLanguage() + "']", node); + String title = selectString(".//title[@lang='" + locale.getLanguage() + "']", node); if (title.isEmpty()) { // English language fall-back title = selectString(".//title[@lang='en']", node); } if (type == 1) { - episodes.add(new Episode(animeTitle, seriesStartDate, null, number, title, number, null, airdate, searchResult)); // normal episode, no seasons for anime + episodes.add(new Episode(animeTitle, seriesStartDate, null, number, title, number, null, SortOrder.Absolute, locale, airdate, searchResult)); // normal episode, no seasons for anime } else { - episodes.add(new Episode(animeTitle, seriesStartDate, null, null, title, null, number, airdate, searchResult)); // special episode + episodes.add(new Episode(animeTitle, seriesStartDate, null, null, title, null, number, SortOrder.Absolute, locale, airdate, searchResult)); // special episode } } } diff --git a/source/net/filebot/web/Episode.java b/source/net/filebot/web/Episode.java index d1cbb140..d6bb4f6f 100644 --- a/source/net/filebot/web/Episode.java +++ b/source/net/filebot/web/Episode.java @@ -3,41 +3,46 @@ package net.filebot.web; import java.io.Serializable; import java.util.Arrays; import java.util.List; +import java.util.Locale; public class Episode implements Serializable { - private String seriesName; - private SimpleDate seriesStartDate; + protected String seriesName; + protected SimpleDate seriesStartDate; - private Integer season; - private Integer episode; - private String title; + protected Integer season; + protected Integer episode; + protected String title; // absolute episode number - private Integer absolute; + protected Integer absolute; // special number - private Integer special; + protected Integer special; + + // optional episode number order hint & episode name / title language hint + protected String order; + protected String language; // episode airdate - private SimpleDate airdate; + protected SimpleDate airdate; // original series descriptor - private SearchResult series; + protected SearchResult series; protected Episode() { // used by serializer } public Episode(Episode obj) { - this(obj.seriesName, obj.seriesStartDate, obj.season, obj.episode, obj.title, obj.absolute, obj.special, obj.airdate, obj.series); + this(obj.seriesName, obj.seriesStartDate, obj.season, obj.episode, obj.title, obj.absolute, obj.special, obj.getOrder(), obj.getLanguage(), obj.airdate, obj.series); } public Episode(String seriesName, SimpleDate seriesStartDate, Integer season, Integer episode, String title, SearchResult series) { - this(seriesName, seriesStartDate, season, episode, title, null, null, null, series); + this(seriesName, seriesStartDate, season, episode, title, null, null, null, null, null, series); } - public Episode(String seriesName, SimpleDate seriesStartDate, Integer season, Integer episode, String title, Integer absolute, Integer special, SimpleDate airdate, SearchResult series) { + public Episode(String seriesName, SimpleDate seriesStartDate, Integer season, Integer episode, String title, Integer absolute, Integer special, SortOrder order, Locale locale, SimpleDate airdate, SearchResult series) { this.seriesName = seriesName; this.seriesStartDate = (seriesStartDate == null ? null : seriesStartDate.clone()); this.season = season; @@ -45,6 +50,8 @@ public class Episode implements Serializable { this.title = title; this.absolute = absolute; this.special = special; + this.order = (order == null ? null : order.name()); + this.language = (locale == null ? null : locale.getLanguage()); this.airdate = (airdate == null ? null : airdate.clone()); this.series = (series == null ? null : series.clone()); } @@ -77,6 +84,14 @@ public class Episode implements Serializable { return special; } + public SortOrder getOrder() { + return order == null ? null : SortOrder.forName(order); + } + + public Locale getLanguage() { + return language == null ? null : new Locale(language); + } + public SimpleDate getAirdate() { return airdate; } diff --git a/source/net/filebot/web/EpisodeFormat.java b/source/net/filebot/web/EpisodeFormat.java index 68dceb37..9b7cd746 100644 --- a/source/net/filebot/web/EpisodeFormat.java +++ b/source/net/filebot/web/EpisodeFormat.java @@ -187,7 +187,7 @@ public class EpisodeFormat extends Format { // did parse input pos.setIndex(source.length()); - return new Episode(name, null, season, episode, title, season == null ? episode : null, special, airdate, null); + return new Episode(name, null, season, episode, title, season == null ? episode : null, special, null, null, airdate, null); } // failed to parse input diff --git a/source/net/filebot/web/EpisodeUtilities.java b/source/net/filebot/web/EpisodeUtilities.java index 149efc7c..93e1bd33 100644 --- a/source/net/filebot/web/EpisodeUtilities.java +++ b/source/net/filebot/web/EpisodeUtilities.java @@ -1,73 +1,66 @@ - package net.filebot.web; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; - public final class EpisodeUtilities { - + public static List filterBySeason(Iterable episodes, int season) { List results = new ArrayList(25); - + // filter given season from all seasons for (Episode episode : episodes) { if (episode.getSeason() != null && season == episode.getSeason()) { results.add(episode); } } - + return results; } - - + public static int getLastSeason(Iterable episodes) { int lastSeason = 0; - + // filter given season from all seasons for (Episode episode : episodes) { if (episode.getSeason() != null && episode.getSeason() > lastSeason) { lastSeason = episode.getSeason(); } } - + return lastSeason; } - - + public static void sortEpisodes(List episodes) { Collections.sort(episodes, episodeComparator()); } - - + public static Comparator episodeComparator() { return new Comparator() { - + @Override public int compare(Episode a, Episode b) { int diff = compareValue(a.getSeriesName(), b.getSeriesName()); if (diff != 0) return diff; - + diff = compareValue(a.getSeason(), b.getSeason()); if (diff != 0) return diff; - + diff = compareValue(a.getEpisode(), b.getEpisode()); if (diff != 0) return diff; - + diff = compareValue(a.getSpecial(), b.getSpecial()); if (diff != 0) return diff; - + return compareValue(a.getTitle(), b.getTitle()); } - - + private int compareValue(Comparable o1, T o2) { if (o1 == null && o2 == null) return 0; @@ -75,13 +68,12 @@ public final class EpisodeUtilities { return Integer.MAX_VALUE; if (o1 != null && o2 == null) return Integer.MIN_VALUE; - + return o1.compareTo(o2); } }; } - - + private EpisodeUtilities() { throw new UnsupportedOperationException(); } diff --git a/source/net/filebot/web/Movie.java b/source/net/filebot/web/Movie.java index 1380a72a..5c9575f8 100644 --- a/source/net/filebot/web/Movie.java +++ b/source/net/filebot/web/Movie.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; public class Movie extends SearchResult { @@ -12,19 +13,27 @@ public class Movie extends SearchResult { protected int imdbId; protected int tmdbId; + // optional movie name language hint + protected String language; + protected Movie() { // used by serializer } - public Movie(String name, int year, int imdbId, int tmdbId) { - this(name, new String[0], year, imdbId, tmdbId); + public Movie(Movie obj) { + this(obj.name, obj.aliasNames, obj.year, obj.imdbId, obj.tmdbId, obj.getLanguage()); } - public Movie(String name, String[] aliasNames, int year, int imdbId, int tmdbId) { + public Movie(String name, int year, int imdbId, int tmdbId) { + this(name, new String[0], year, imdbId, tmdbId, null); + } + + public Movie(String name, String[] aliasNames, int year, int imdbId, int tmdbId, Locale locale) { super(name, aliasNames); this.year = year; this.imdbId = imdbId; this.tmdbId = tmdbId; + this.language = (locale == null ? null : locale.getLanguage()); } public int getYear() { @@ -39,6 +48,10 @@ public class Movie extends SearchResult { return tmdbId; } + public Locale getLanguage() { + return language == null ? null : new Locale(language); + } + public String getNameWithYear() { return toString(name, year); } @@ -86,7 +99,7 @@ public class Movie extends SearchResult { @Override public Movie clone() { - return new Movie(name, aliasNames, year, imdbId, tmdbId); + return new Movie(this); } @Override diff --git a/source/net/filebot/web/MoviePart.java b/source/net/filebot/web/MoviePart.java index b84cb13b..edeaa54a 100644 --- a/source/net/filebot/web/MoviePart.java +++ b/source/net/filebot/web/MoviePart.java @@ -1,60 +1,46 @@ - package net.filebot.web; - public class MoviePart extends Movie { - + protected final int partIndex; protected final int partCount; - - + public MoviePart(MoviePart obj) { - this(obj.name, obj.year, obj.imdbId, obj.tmdbId, obj.partIndex, obj.partCount); + this(obj, obj.partIndex, obj.partCount); } - - + public MoviePart(Movie movie, int partIndex, int partCount) { - this(movie.name, movie.year, movie.imdbId, movie.tmdbId, partIndex, partCount); - } - - - public MoviePart(String name, int year, int imdbId, int tmdbId, int partIndex, int partCount) { - super(name, year, imdbId, tmdbId); + super(movie); this.partIndex = partIndex; this.partCount = partCount; } - - + public int getPartIndex() { return partIndex; } - - + public int getPartCount() { return partCount; } - - + @Override public boolean equals(Object object) { if (object instanceof MoviePart && super.equals(object)) { MoviePart other = (MoviePart) object; return partIndex == other.partIndex && partCount == other.partCount; } - + return super.equals(object); } - - + @Override public MoviePart clone() { return new MoviePart(this); } - - + @Override public String toString() { return String.format("%s (%d) [%d]", name, year, partIndex); } - + } diff --git a/source/net/filebot/web/SerienjunkiesClient.java b/source/net/filebot/web/SerienjunkiesClient.java index c405e2e4..9b3e6b66 100644 --- a/source/net/filebot/web/SerienjunkiesClient.java +++ b/source/net/filebot/web/SerienjunkiesClient.java @@ -141,7 +141,11 @@ public class SerienjunkiesClient extends AbstractEpisodeListProvider { title = ""; } - episodes.add(new Episode(seriesName, series.getStartDate(), season, episode, title, i + 1, null, airdate, searchResult)); + // additional metadata + SortOrder order = SortOrder.Airdate; + Locale language = Locale.GERMAN.equals(locale) ? Locale.GERMAN : Locale.ENGLISH; + + episodes.add(new Episode(seriesName, series.getStartDate(), season, episode, title, i + 1, null, order, language, airdate, searchResult)); } // make sure episodes are in ordered correctly diff --git a/source/net/filebot/web/SortOrder.java b/source/net/filebot/web/SortOrder.java index a3f653b6..1b1bd884 100644 --- a/source/net/filebot/web/SortOrder.java +++ b/source/net/filebot/web/SortOrder.java @@ -1,23 +1,19 @@ - package net.filebot.web; - public enum SortOrder { - Airdate, - DVD, - Absolute; - + + Airdate, DVD, Absolute; + public static SortOrder forName(String name) { for (SortOrder order : SortOrder.values()) { if (order.name().equalsIgnoreCase(name)) { return order; } } - + throw new IllegalArgumentException("Invalid SortOrder: " + name); } - - + @Override public String toString() { return String.format("%s Order", name()); diff --git a/source/net/filebot/web/TMDbClient.java b/source/net/filebot/web/TMDbClient.java index c638ab6b..d22740b2 100644 --- a/source/net/filebot/web/TMDbClient.java +++ b/source/net/filebot/web/TMDbClient.java @@ -129,7 +129,7 @@ public class TMDbClient implements MovieIdentificationService { // make sure main title is not in the set of alternative titles alternativeTitles.remove(title); - result.add(new Movie(title, alternativeTitles.toArray(new String[0]), year, -1, id)); + result.add(new Movie(title, alternativeTitles.toArray(new String[0]), year, -1, id, locale)); } catch (Exception e) { // only print 'missing release date' warnings for matching movie titles if (query.equalsIgnoreCase(title) || query.equalsIgnoreCase(originalTitle)) { @@ -157,7 +157,7 @@ public class TMDbClient implements MovieIdentificationService { String id = byIMDB ? String.format("tt%07d", imdbtmdbid) : String.valueOf(imdbtmdbid); try { MovieInfo info = getMovieInfo(id, locale, false, false); - return new Movie(info.getName(), info.getReleased().getYear(), info.getImdbId(), info.getId()); + return new Movie(info.getName(), new String[0], info.getReleased().getYear(), info.getImdbId(), info.getId(), locale); } catch (FileNotFoundException e) { Logger.getLogger(getClass().getName()).log(Level.WARNING, "Movie not found: " + id); return null; diff --git a/source/net/filebot/web/TVRageClient.java b/source/net/filebot/web/TVRageClient.java index b9d4d83d..ae56a136 100644 --- a/source/net/filebot/web/TVRageClient.java +++ b/source/net/filebot/web/TVRageClient.java @@ -1,7 +1,5 @@ - package net.filebot.web; - import static net.filebot.util.XPathUtilities.*; import static net.filebot.web.EpisodeUtilities.*; import static net.filebot.web.WebRequest.*; @@ -22,63 +20,58 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.SAXException; - public class TVRageClient extends AbstractEpisodeListProvider { - + private final String host = "services.tvrage.com"; - - + @Override public String getName() { return "TVRage"; } - - + @Override public Icon getIcon() { return ResourceManager.getIcon("search.tvrage"); } - - + @Override public ResultCache getCache() { return new ResultCache(host, Cache.getCache("web-datasource")); } - - + @Override public List fetchSearchResult(String query, Locale locale) throws IOException, SAXException { URL searchUrl = new URL("http", host, "/feeds/full_search.php?show=" + encode(query, true)); Document dom = getDocument(searchUrl); - + List nodes = selectNodes("Results/show", dom); List searchResults = new ArrayList(nodes.size()); - + for (Node node : nodes) { int showid = Integer.parseInt(getTextContent("showid", node)); String name = getTextContent("name", node); String link = getTextContent("link", node); - + searchResults.add(new TVRageSearchResult(name, showid, link)); } - + return searchResults; } - - + @Override public List fetchEpisodeList(SearchResult searchResult, SortOrder sortOrder, Locale locale) throws IOException, SAXException { TVRageSearchResult series = (TVRageSearchResult) searchResult; - + URL episodeListUrl = new URL("http", host, "/feeds/full_show_info.php?sid=" + series.getSeriesId()); Document dom = getDocument(episodeListUrl); - + String seriesName = selectString("Show/name", dom); SimpleDate seriesStartDate = SimpleDate.parse(selectString("Show/started", dom), "MMM/dd/yyyy"); - + Locale language = Locale.ENGLISH; + List episodes = new ArrayList(25); List specials = new ArrayList(5); - + // episodes and specials for (Node node : selectNodes("//episode", dom)) { String title = getTextContent("title", node); @@ -86,34 +79,36 @@ public class TVRageClient extends AbstractEpisodeListProvider { String seasonIdentifier = getAttribute("no", node.getParentNode()); Integer seasonNumber = seasonIdentifier == null ? null : new Integer(seasonIdentifier); SimpleDate airdate = SimpleDate.parse(getTextContent("airdate", node), "yyyy-MM-dd"); - + + SortOrder order = SortOrder.Airdate; // default order + // check if we have season and episode number, if not it must be a special episode if (episodeNumber == null || seasonNumber == null) { // handle as special episode seasonNumber = getIntegerContent("season", node); int specialNumber = filterBySeason(specials, seasonNumber).size() + 1; - specials.add(new Episode(seriesName, seriesStartDate, seasonNumber, null, title, null, specialNumber, airdate, searchResult)); + specials.add(new Episode(seriesName, seriesStartDate, seasonNumber, null, title, null, specialNumber, order, language, airdate, searchResult)); } else { // handle as normal episode if (sortOrder == SortOrder.Absolute) { episodeNumber = getIntegerContent("epnum", node); seasonNumber = null; + order = SortOrder.Absolute; } - - episodes.add(new Episode(seriesName, seriesStartDate, seasonNumber, episodeNumber, title, null, null, airdate, searchResult)); + + episodes.add(new Episode(seriesName, seriesStartDate, seasonNumber, episodeNumber, title, null, null, order, language, airdate, searchResult)); } } - + // add specials at the end episodes.addAll(specials); - + return episodes; } - - + @Override public URI getEpisodeListLink(SearchResult searchResult) { return URI.create(((TVRageSearchResult) searchResult).getLink() + "/episode_list/all"); } - + } diff --git a/source/net/filebot/web/TheTVDBClient.java b/source/net/filebot/web/TheTVDBClient.java index d329d6c7..ef4de516 100644 --- a/source/net/filebot/web/TheTVDBClient.java +++ b/source/net/filebot/web/TheTVDBClient.java @@ -139,6 +139,7 @@ public class TheTVDBClient extends AbstractEpisodeListProvider { // default numbering Integer episodeNumber = getIntegerContent("EpisodeNumber", node); Integer seasonNumber = getIntegerContent("SeasonNumber", node); + SortOrder order = SortOrder.Airdate; if (seasonNumber == null || seasonNumber == 0) { // handle as special episode @@ -149,24 +150,30 @@ public class TheTVDBClient extends AbstractEpisodeListProvider { // use given episode number as special number or count specials by ourselves Integer specialNumber = (episodeNumber != null) ? episodeNumber : filterBySeason(specials, seasonNumber).size() + 1; - specials.add(new Episode(seriesName, seriesStartDate, seasonNumber, null, episodeName, null, specialNumber, airdate, searchResult)); + specials.add(new Episode(seriesName, seriesStartDate, seasonNumber, null, episodeName, null, specialNumber, order, locale, airdate, searchResult)); } else { // handle as normal episode if (sortOrder == SortOrder.Absolute) { if (absoluteNumber != null) { episodeNumber = absoluteNumber; seasonNumber = null; + order = SortOrder.Absolute; } } else if (sortOrder == SortOrder.DVD) { try { - episodeNumber = new Float(dvdEpisodeNumber).intValue(); - seasonNumber = new Integer(dvdSeasonNumber); + int eno = new Float(dvdEpisodeNumber).intValue(); + int sno = new Float(dvdSeasonNumber).intValue(); + + // require both values to be successfully read + episodeNumber = eno; + seasonNumber = sno; + order = SortOrder.DVD; } catch (Exception e) { // ignore, fallback to default numbering } } - episodes.add(new Episode(seriesName, seriesStartDate, seasonNumber, episodeNumber, episodeName, absoluteNumber, null, airdate, searchResult)); + episodes.add(new Episode(seriesName, seriesStartDate, seasonNumber, episodeNumber, episodeName, absoluteNumber, null, order, locale, airdate, searchResult)); } }