diff --git a/source/net/filebot/WebServices.java b/source/net/filebot/WebServices.java index 1fd64b87..b09ed6af 100644 --- a/source/net/filebot/WebServices.java +++ b/source/net/filebot/WebServices.java @@ -35,6 +35,7 @@ import net.filebot.web.ShooterSubtitles; import net.filebot.web.SubtitleProvider; import net.filebot.web.SubtitleSearchResult; import net.filebot.web.TMDbClient; +import net.filebot.web.TMDbTVClient; import net.filebot.web.TVMazeClient; import net.filebot.web.TheTVDBClient; import net.filebot.web.VideoHashSubtitleService; @@ -45,28 +46,30 @@ import one.util.streamex.StreamEx; */ public final class WebServices { - // episode dbs + // movie sources + public static final OMDbClient OMDb = new OMDbClient(); + public static final TMDbClient TheMovieDB = new TMDbClient(getApiKey("themoviedb")); + + // episode sources public static final TVMazeClient TVmaze = new TVMazeClient(); public static final AnidbClient AniDB = new AnidbClientWithLocalSearch(getApiKey("anidb"), 6); // extended TheTVDB module with local search public static final TheTVDBClientWithLocalSearch TheTVDB = new TheTVDBClientWithLocalSearch(getApiKey("thetvdb")); + public static final TMDbTVClient TheMovieDB_TV = new TMDbTVClient(TheMovieDB); - // movie dbs - public static final OMDbClient OMDb = new OMDbClient(); - public static final TMDbClient TheMovieDB = new TMDbClient(getApiKey("themoviedb")); - - // subtitle dbs + // subtitle sources public static final OpenSubtitlesClient OpenSubtitles = new OpenSubtitlesClientWithLocalSearch(getApiKey("opensubtitles"), getApplicationVersion()); public static final ShooterSubtitles Shooter = new ShooterSubtitles(); - // misc + // other sources public static final FanartTVClient FanartTV = new FanartTVClient(Settings.getApiKey("fanart.tv")); public static final AcoustIDClient AcoustID = new AcoustIDClient(Settings.getApiKey("acoustid")); public static final XattrMetaInfoProvider XattrMetaData = new XattrMetaInfoProvider(); + public static final ID3Lookup MediaInfoID3 = new ID3Lookup(); public static EpisodeListProvider[] getEpisodeListProviders() { - return new EpisodeListProvider[] { TheTVDB, TheMovieDB, AniDB, TVmaze }; + return new EpisodeListProvider[] { TheTVDB, TheMovieDB_TV, AniDB, TVmaze }; } public static MovieIdentificationService[] getMovieIdentificationServices() { @@ -85,16 +88,10 @@ public final class WebServices { } public static MusicIdentificationService[] getMusicIdentificationServices() { - return new MusicIdentificationService[] { AcoustID, new ID3Lookup() }; + return new MusicIdentificationService[] { AcoustID, MediaInfoID3 }; } public static EpisodeListProvider getEpisodeListProvider(String name) { - // special handling for TheMovieDB which is the only datasource that supports both series and movie mode - if (name.equalsIgnoreCase(TheMovieDB.getName())) - return null; - if (name.equalsIgnoreCase(TheMovieDB.getName() + "::TV")) - return TheMovieDB; - return getService(name, getEpisodeListProviders()); } @@ -107,7 +104,7 @@ public final class WebServices { } private static T getService(String name, T[] services) { - return StreamEx.of(services).findFirst(it -> it.getName().equalsIgnoreCase(name)).orElse(null); + return StreamEx.of(services).findFirst(it -> it.getIdentifier().equalsIgnoreCase(name)).orElse(null); } public static final ExecutorService requestThreadPool = Executors.newCachedThreadPool(); diff --git a/source/net/filebot/cli/CmdlineOperations.java b/source/net/filebot/cli/CmdlineOperations.java index 59336ea0..c674d274 100644 --- a/source/net/filebot/cli/CmdlineOperations.java +++ b/source/net/filebot/cli/CmdlineOperations.java @@ -94,16 +94,16 @@ public class CmdlineOperations implements CmdlineInterface { Locale locale = getLanguage(lang).getLocale(); ConflictAction conflictAction = ConflictAction.forName(conflict); - if (getEpisodeListProvider(db) != null) { - // tv series mode - return renameSeries(files, action, conflictAction, outputDir, format, getEpisodeListProvider(db), query, SortOrder.forName(sortOrder), filter, locale, strict); - } - if (getMovieIdentificationService(db) != null) { // movie mode return renameMovie(files, action, conflictAction, outputDir, format, getMovieIdentificationService(db), query, filter, locale, strict); } + if (getEpisodeListProvider(db) != null) { + // tv series mode + return renameSeries(files, action, conflictAction, outputDir, format, getEpisodeListProvider(db), query, SortOrder.forName(sortOrder), filter, locale, strict); + } + if (getMusicIdentificationService(db) != null || containsOnly(files, AUDIO_FILES)) { // music mode return renameMusic(files, action, conflictAction, outputDir, format, getMusicIdentificationService(db) == null ? AcoustID : getMusicIdentificationService(db)); diff --git a/source/net/filebot/format/MediaBindingBean.java b/source/net/filebot/format/MediaBindingBean.java index 7e2ab3da..09a62fd4 100644 --- a/source/net/filebot/format/MediaBindingBean.java +++ b/source/net/filebot/format/MediaBindingBean.java @@ -245,7 +245,7 @@ public class MediaBindingBean { if (infoObject instanceof Episode) { // force English series name for TheTVDB data - if (WebServices.TheTVDB.getName().equals(getSeriesInfo().getDatabase())) { + if (WebServices.TheTVDB.getIdentifier().equals(getSeriesInfo().getDatabase())) { return WebServices.TheTVDB.getSeriesInfo(getSeriesInfo().getId(), Locale.ENGLISH).getName(); } @@ -628,7 +628,7 @@ public class MediaBindingBean { try { if (infoObject instanceof Episode) { - if (WebServices.TheTVDB.getName().equals(getSeriesInfo().getDatabase())) { + if (WebServices.TheTVDB.getIdentifier().equals(getSeriesInfo().getDatabase())) { TheTVDBSeriesInfo extendedSeriesInfo = (TheTVDBSeriesInfo) WebServices.TheTVDB.getSeriesInfo(getSeriesInfo().getId(), Locale.ENGLISH); if (extendedSeriesInfo.getImdbId() != null) { metaInfo = WebServices.OMDb.getMovieInfo(new Movie(null, -1, grepImdbId(extendedSeriesInfo.getImdbId()).iterator().next(), -1)); diff --git a/source/net/filebot/ui/rename/PlainFileMatcher.java b/source/net/filebot/ui/rename/PlainFileMatcher.java index 91443e5f..fcbd760a 100644 --- a/source/net/filebot/ui/rename/PlainFileMatcher.java +++ b/source/net/filebot/ui/rename/PlainFileMatcher.java @@ -15,7 +15,9 @@ import net.filebot.web.SortOrder; public class PlainFileMatcher implements Datasource, AutoCompleteMatcher { - public static final PlainFileMatcher INSTANCE = new PlainFileMatcher(); + public static PlainFileMatcher getInstance() { + return new PlainFileMatcher(); + } @Override public String getName() { diff --git a/source/net/filebot/ui/rename/Preset.java b/source/net/filebot/ui/rename/Preset.java index f3657373..4b86581b 100644 --- a/source/net/filebot/ui/rename/Preset.java +++ b/source/net/filebot/ui/rename/Preset.java @@ -39,7 +39,7 @@ public class Preset { this.path = path == null ? null : path.getPath(); this.includes = includes == null ? null : includes.getExpression(); this.format = format == null ? null : format.getExpression(); - this.database = database == null ? null : database.getName(); + this.database = database == null ? null : database.getIdentifier(); this.sortOrder = sortOrder == null ? null : sortOrder.name(); this.matchMode = matchMode == null ? null : matchMode; this.language = language == null ? null : language.getCode(); @@ -104,8 +104,8 @@ public class Preset { if (adb != null) { return new AudioFingerprintMatcher(adb); } - if (PlainFileMatcher.INSTANCE.getName().equals(database)) { - return PlainFileMatcher.INSTANCE; + if (PlainFileMatcher.getInstance().getIdentifier().equals(database)) { + return PlainFileMatcher.getInstance(); } throw new IllegalStateException(database); } @@ -114,20 +114,20 @@ public class Preset { if (database == null || database.isEmpty()) { return null; } - EpisodeListProvider sdb = WebServices.getEpisodeListProvider(database); - if (sdb != null) { - return sdb; - } MovieIdentificationService mdb = WebServices.getMovieIdentificationService(database); if (mdb != null) { return mdb; } + EpisodeListProvider sdb = WebServices.getEpisodeListProvider(database); + if (sdb != null) { + return sdb; + } MusicIdentificationService adb = WebServices.getMusicIdentificationService(database); if (adb != null) { return adb; } - if (PlainFileMatcher.INSTANCE.getName().equals(database)) { - return PlainFileMatcher.INSTANCE; + if (PlainFileMatcher.getInstance().getIdentifier().equals(database)) { + return PlainFileMatcher.getInstance(); } throw new IllegalStateException(database); } diff --git a/source/net/filebot/ui/rename/PresetEditor.java b/source/net/filebot/ui/rename/PresetEditor.java index ee5f125c..e6cc74a5 100644 --- a/source/net/filebot/ui/rename/PresetEditor.java +++ b/source/net/filebot/ui/rename/PresetEditor.java @@ -33,6 +33,11 @@ import javax.swing.ListCellRenderer; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; +import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.fife.ui.rtextarea.RTextScrollPane; + import net.filebot.Language; import net.filebot.RenameAction; import net.filebot.ResourceManager; @@ -50,11 +55,6 @@ import net.filebot.web.MovieIdentificationService; import net.filebot.web.SortOrder; import net.miginfocom.swing.MigLayout; -import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.SyntaxConstants; -import org.fife.ui.rtextarea.RTextScrollPane; - public class PresetEditor extends JDialog { enum Result { @@ -154,7 +154,7 @@ public class PresetEditor extends JDialog { boolean input = selectRadio.isSelected(); inputPanel.setVisible(input); - sortOrderCombo.setEnabled(ds instanceof EpisodeListProvider && ((EpisodeListProvider) ds).hasSeasonSupport()); + sortOrderCombo.setEnabled(ds instanceof EpisodeListProvider); languageCombo.setEnabled(ds instanceof EpisodeListProvider || ds instanceof MovieIdentificationService); matchModeCombo.setEnabled(ds instanceof EpisodeListProvider || ds instanceof MovieIdentificationService); } @@ -247,7 +247,7 @@ public class PresetEditor extends JDialog { providers.addElement(it); } } - providers.addElement(PlainFileMatcher.INSTANCE); + providers.addElement(PlainFileMatcher.getInstance()); JComboBox combo = new JComboBox(providers); combo.setRenderer(new ListCellRenderer() { diff --git a/source/net/filebot/web/AnidbClient.java b/source/net/filebot/web/AnidbClient.java index 7094d815..0d0eb041 100644 --- a/source/net/filebot/web/AnidbClient.java +++ b/source/net/filebot/web/AnidbClient.java @@ -104,7 +104,7 @@ public class AnidbClient extends AbstractEpisodeListProvider { Document dom = getDocument(url); // parse series info - SeriesInfo seriesInfo = new SeriesInfo(getName(), sortOrder, locale, anime.getId()); + SeriesInfo seriesInfo = new SeriesInfo(this, sortOrder, locale, anime.getId()); seriesInfo.setAliasNames(anime.getAliasNames()); // AniDB types: Movie, Music Video, Other, OVA, TV Series, TV Special, Web, unknown diff --git a/source/net/filebot/web/Datasource.java b/source/net/filebot/web/Datasource.java index 45f17ced..7e0ec813 100644 --- a/source/net/filebot/web/Datasource.java +++ b/source/net/filebot/web/Datasource.java @@ -8,4 +8,8 @@ public interface Datasource { Icon getIcon(); + default String getIdentifier() { + return getName(); + } + } diff --git a/source/net/filebot/web/SeriesInfo.java b/source/net/filebot/web/SeriesInfo.java index 40eb5c5f..48f1a9da 100644 --- a/source/net/filebot/web/SeriesInfo.java +++ b/source/net/filebot/web/SeriesInfo.java @@ -50,8 +50,8 @@ public class SeriesInfo implements Serializable { this.status = other.status; } - public SeriesInfo(String database, SortOrder order, Locale language, Integer id) { - this.database = database; + public SeriesInfo(Datasource database, SortOrder order, Locale language, Integer id) { + this.database = database.getIdentifier(); this.order = order.name(); this.language = language.getLanguage(); this.id = id; diff --git a/source/net/filebot/web/TMDbClient.java b/source/net/filebot/web/TMDbClient.java index 4a598a0b..2e5b85d9 100644 --- a/source/net/filebot/web/TMDbClient.java +++ b/source/net/filebot/web/TMDbClient.java @@ -7,7 +7,6 @@ import static net.filebot.CachedResource.*; import static net.filebot.Logging.*; import static net.filebot.util.JsonUtilities.*; import static net.filebot.util.StringUtilities.*; -import static net.filebot.web.EpisodeUtilities.*; import static net.filebot.web.WebRequest.*; import java.io.File; @@ -41,13 +40,13 @@ import net.filebot.ResourceManager; import net.filebot.web.TMDbClient.MovieInfo.MovieProperty; import net.filebot.web.TMDbClient.Person.PersonProperty; -public class TMDbClient extends AbstractEpisodeListProvider implements MovieIdentificationService { +public class TMDbClient implements MovieIdentificationService { private static final String host = "api.themoviedb.org"; private static final String version = "3"; - private static final FloodLimit SEARCH_LIMIT = new FloodLimit(10, 10, TimeUnit.SECONDS); - private static final FloodLimit REQUEST_LIMIT = new FloodLimit(20, 10, TimeUnit.SECONDS); + protected static final FloodLimit SEARCH_LIMIT = new FloodLimit(10, 10, TimeUnit.SECONDS); + protected static final FloodLimit REQUEST_LIMIT = new FloodLimit(20, 10, TimeUnit.SECONDS); private final String apikey; @@ -65,7 +64,7 @@ public class TMDbClient extends AbstractEpisodeListProvider implements MovieIden return ResourceManager.getIcon("search.themoviedb"); } - private Matcher getNameYearMatcher(String query) { + protected Matcher getNameYearMatcher(String query) { return Pattern.compile("(.+)\\b[(]?((?:19|20)\\d{2})[)]?$").matcher(query.trim()); } @@ -116,7 +115,7 @@ public class TMDbClient extends AbstractEpisodeListProvider implements MovieIden }).filter(Objects::nonNull).collect(toList()); } - private Set getAlternativeTitles(String path, String key, String title, String originalTitle, boolean extendedInfo) { + protected Set getAlternativeTitles(String path, String key, String title, String originalTitle, boolean extendedInfo) { Set alternativeTitles = new LinkedHashSet(); if (originalTitle != null) { alternativeTitles.add(originalTitle); @@ -301,7 +300,7 @@ public class TMDbClient extends AbstractEpisodeListProvider implements MovieIden }).collect(toList()); } - public Object request(String resource, Map parameters, Locale locale, final FloodLimit limit) throws Exception { + protected Object request(String resource, Map parameters, Locale locale, final FloodLimit limit) throws Exception { // default parameters String key = parameters.isEmpty() ? resource : resource + '?' + encodeParameters(parameters, true); String cacheName = locale.getLanguage().isEmpty() ? getName() : getName() + "_" + locale; @@ -726,107 +725,4 @@ public class TMDbClient extends AbstractEpisodeListProvider implements MovieIden } - @Override - public boolean hasSeasonSupport() { - return true; - } - - @Override - protected SortOrder vetoRequestParameter(SortOrder order) { - return SortOrder.Airdate; - } - - @Override - public URI getEpisodeListLink(SearchResult searchResult) { - return URI.create("https://www.themoviedb.org/tv/" + searchResult.getId()); - } - - @Override - protected List fetchSearchResult(String query, Locale locale) throws Exception { - // query by name with year filter if possible - Matcher nameYear = getNameYearMatcher(query); - if (nameYear.matches()) { - return searchTV(nameYear.group(1).trim(), Integer.parseInt(nameYear.group(2)), locale, true); - } else { - return searchTV(query.trim(), -1, locale, true); - } - } - - public List searchTV(String seriesName, int startYear, Locale locale, boolean extendedInfo) throws Exception { - Map query = new LinkedHashMap(2); - query.put("query", seriesName); - if (startYear > 0) { - query.put("first_air_date_year", startYear); - } - Object response = request("search/tv", query, locale, SEARCH_LIMIT); - - return streamJsonObjects(response, "results").map(it -> { - Integer id = getInteger(it, "id"); - String name = getString(it, "name"); - String originalName = getString(it, "original_name"); - - if (name == null) { - name = originalName; - } - - if (id == null || name == null) { - return null; - } - - Set alternativeTitles = getAlternativeTitles("tv/" + id, "results", name, originalName, extendedInfo); - - return new SearchResult(id, name, alternativeTitles); - }).filter(Objects::nonNull).collect(toList()); - } - - @Override - protected SeriesData fetchSeriesData(SearchResult series, SortOrder sortOrder, Locale locale) throws Exception { - // http://api.themoviedb.org/3/tv/id - Object tv = request("tv/" + series.getId(), emptyMap(), locale, REQUEST_LIMIT); - - SeriesInfo info = new SeriesInfo(getName(), sortOrder, locale, series.getId()); - info.setName(Stream.of("original_name", "name").map(key -> getString(tv, key)).filter(Objects::nonNull).findFirst().orElse(series.getName())); - info.setAliasNames(series.getAliasNames()); - info.setStatus(getString(tv, "status")); - info.setLanguage(getString(tv, "original_language")); - info.setStartDate(getStringValue(tv, "first_air_date", SimpleDate::parse)); - info.setRating(getStringValue(tv, "vote_average", Double::new)); - info.setRatingCount(getStringValue(tv, "vote_count", Integer::new)); - info.setRuntime(stream(getArray(tv, "episode_run_time")).map(Object::toString).map(Integer::new).findFirst().orElse(null)); - info.setGenres(streamJsonObjects(tv, "genres").map(it -> getString(it, "name")).collect(toList())); - info.setNetwork(streamJsonObjects(tv, "networks").map(it -> getString(it, "name")).findFirst().orElse(null)); - - int[] seasons = streamJsonObjects(tv, "seasons").mapToInt(it -> getInteger(it, "season_number")).toArray(); - List episodes = new ArrayList(); - List specials = new ArrayList(); - - for (int s : seasons) { - // http://api.themoviedb.org/3/tv/id/season/season_number - Object season = request("tv/" + series.getId() + "/season/" + s, emptyMap(), locale, REQUEST_LIMIT); - - streamJsonObjects(season, "episodes").forEach(episode -> { - Integer episodeNumber = getInteger(episode, "episode_number"); - Integer seasonNumber = getInteger(episode, "season_number"); - String episodeTitle = getString(episode, "name"); - SimpleDate airdate = getStringValue(episode, "air_date", SimpleDate::parse); - - Integer absoluteNumber = episodes.size() + 1; - - if (s > 0) { - episodes.add(new Episode(series.getName(), seasonNumber, episodeNumber, episodeTitle, absoluteNumber, null, airdate, info)); - } else { - specials.add(new Episode(series.getName(), null, null, episodeTitle, null, episodeNumber, airdate, info)); - } - }); - } - - // episodes my not be ordered by DVD episode number - episodes.sort(episodeComparator()); - - // add specials at the end - episodes.addAll(specials); - - return new SeriesData(info, episodes); - } - } diff --git a/source/net/filebot/web/TMDbTVClient.java b/source/net/filebot/web/TMDbTVClient.java new file mode 100644 index 00000000..a6185ee7 --- /dev/null +++ b/source/net/filebot/web/TMDbTVClient.java @@ -0,0 +1,149 @@ +package net.filebot.web; + +import static java.util.Arrays.*; +import static java.util.Collections.*; +import static java.util.stream.Collectors.*; +import static net.filebot.util.JsonUtilities.*; +import static net.filebot.web.EpisodeUtilities.*; +import static net.filebot.web.TMDbClient.*; + +import java.net.URI; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.stream.Stream; + +import javax.swing.Icon; + +public class TMDbTVClient extends AbstractEpisodeListProvider { + + private final TMDbClient tmdb; + + public TMDbTVClient(TMDbClient tmdb) { + this.tmdb = tmdb; + } + + @Override + public String getName() { + return tmdb.getName(); + } + + @Override + public String getIdentifier() { + return "TheMovieDB::TV"; + } + + @Override + public Icon getIcon() { + return tmdb.getIcon(); + } + + @Override + public boolean hasSeasonSupport() { + return true; + } + + @Override + protected SortOrder vetoRequestParameter(SortOrder order) { + return SortOrder.Airdate; + } + + @Override + public URI getEpisodeListLink(SearchResult searchResult) { + return URI.create("https://www.themoviedb.org/tv/" + searchResult.getId()); + } + + @Override + protected List fetchSearchResult(String query, Locale locale) throws Exception { + // query by name with year filter if possible + Matcher nameYear = tmdb.getNameYearMatcher(query); + if (nameYear.matches()) { + return searchTV(nameYear.group(1).trim(), Integer.parseInt(nameYear.group(2)), locale, true); + } else { + return searchTV(query.trim(), -1, locale, true); + } + } + + public List searchTV(String seriesName, int startYear, Locale locale, boolean extendedInfo) throws Exception { + Map query = new LinkedHashMap(2); + query.put("query", seriesName); + if (startYear > 0) { + query.put("first_air_date_year", startYear); + } + Object response = tmdb.request("search/tv", query, locale, SEARCH_LIMIT); + + return streamJsonObjects(response, "results").map(it -> { + Integer id = getInteger(it, "id"); + String name = getString(it, "name"); + String originalName = getString(it, "original_name"); + + if (name == null) { + name = originalName; + } + + if (id == null || name == null) { + return null; + } + + Set alternativeTitles = tmdb.getAlternativeTitles("tv/" + id, "results", name, originalName, extendedInfo); + + return new SearchResult(id, name, alternativeTitles); + }).filter(Objects::nonNull).collect(toList()); + } + + @Override + protected SeriesData fetchSeriesData(SearchResult series, SortOrder sortOrder, Locale locale) throws Exception { + // http://api.themoviedb.org/3/tv/id + Object tv = tmdb.request("tv/" + series.getId(), emptyMap(), locale, REQUEST_LIMIT); + + SeriesInfo info = new SeriesInfo(this, sortOrder, locale, series.getId()); + info.setName(Stream.of("original_name", "name").map(key -> getString(tv, key)).filter(Objects::nonNull).findFirst().orElse(series.getName())); + info.setAliasNames(series.getAliasNames()); + info.setStatus(getString(tv, "status")); + info.setLanguage(getString(tv, "original_language")); + info.setStartDate(getStringValue(tv, "first_air_date", SimpleDate::parse)); + info.setRating(getStringValue(tv, "vote_average", Double::new)); + info.setRatingCount(getStringValue(tv, "vote_count", Integer::new)); + info.setRuntime(stream(getArray(tv, "episode_run_time")).map(Object::toString).map(Integer::new).findFirst().orElse(null)); + info.setGenres(streamJsonObjects(tv, "genres").map(it -> getString(it, "name")).collect(toList())); + info.setNetwork(streamJsonObjects(tv, "networks").map(it -> getString(it, "name")).findFirst().orElse(null)); + + int[] seasons = streamJsonObjects(tv, "seasons").mapToInt(it -> getInteger(it, "season_number")).toArray(); + List episodes = new ArrayList(); + List specials = new ArrayList(); + + for (int s : seasons) { + // http://api.themoviedb.org/3/tv/id/season/season_number + Object season = tmdb.request("tv/" + series.getId() + "/season/" + s, emptyMap(), locale, REQUEST_LIMIT); + + streamJsonObjects(season, "episodes").forEach(episode -> { + Integer episodeNumber = getInteger(episode, "episode_number"); + Integer seasonNumber = getInteger(episode, "season_number"); + String episodeTitle = getString(episode, "name"); + SimpleDate airdate = getStringValue(episode, "air_date", SimpleDate::parse); + + Integer absoluteNumber = episodes.size() + 1; + + if (s > 0) { + episodes.add(new Episode(series.getName(), seasonNumber, episodeNumber, episodeTitle, absoluteNumber, null, airdate, info)); + } else { + specials.add(new Episode(series.getName(), null, null, episodeTitle, null, episodeNumber, airdate, info)); + } + }); + } + + // episodes my not be ordered by DVD episode number + episodes.sort(episodeComparator()); + + // add specials at the end + episodes.addAll(specials); + + return new SeriesData(info, episodes); + } + +} diff --git a/source/net/filebot/web/TVMazeClient.java b/source/net/filebot/web/TVMazeClient.java index 81f14163..04b943aa 100644 --- a/source/net/filebot/web/TVMazeClient.java +++ b/source/net/filebot/web/TVMazeClient.java @@ -69,7 +69,7 @@ public class TVMazeClient extends AbstractEpisodeListProvider { Object[] genres = getArray(response, "genres"); Double rating = getStringValue(getMap(response, "rating"), "average", Double::new); - SeriesInfo seriesInfo = new SeriesInfo(getName(), sortOrder, locale, show.getId()); + SeriesInfo seriesInfo = new SeriesInfo(this, sortOrder, locale, show.getId()); seriesInfo.setName(show.getName()); seriesInfo.setAliasNames(show.getAliasNames()); seriesInfo.setStatus(status); diff --git a/source/net/filebot/web/TheTVDBClient.java b/source/net/filebot/web/TheTVDBClient.java index 853a3186..92f92796 100644 --- a/source/net/filebot/web/TheTVDBClient.java +++ b/source/net/filebot/web/TheTVDBClient.java @@ -121,7 +121,7 @@ public class TheTVDBClient extends AbstractEpisodeListProvider { // parse series info Node seriesNode = selectNode("Data/Series", dom); - TheTVDBSeriesInfo seriesInfo = new TheTVDBSeriesInfo(getName(), sortOrder, locale, series.getId()); + TheTVDBSeriesInfo seriesInfo = new TheTVDBSeriesInfo(this, sortOrder, locale, series.getId()); seriesInfo.setAliasNames(series.getAliasNames()); seriesInfo.setName(getTextContent("SeriesName", seriesNode)); diff --git a/source/net/filebot/web/TheTVDBSeriesInfo.java b/source/net/filebot/web/TheTVDBSeriesInfo.java index a9bd689f..2e9e9bdb 100644 --- a/source/net/filebot/web/TheTVDBSeriesInfo.java +++ b/source/net/filebot/web/TheTVDBSeriesInfo.java @@ -17,7 +17,7 @@ public class TheTVDBSeriesInfo extends SeriesInfo implements Serializable { protected String posterUrl; protected TheTVDBSeriesInfo() { - super(); + } public TheTVDBSeriesInfo(TheTVDBSeriesInfo other) { @@ -31,7 +31,7 @@ public class TheTVDBSeriesInfo extends SeriesInfo implements Serializable { this.posterUrl = other.posterUrl; } - public TheTVDBSeriesInfo(String database, SortOrder order, Locale language, Integer id) { + public TheTVDBSeriesInfo(Datasource database, SortOrder order, Locale language, Integer id) { super(database, order, language, id); } diff --git a/test/net/filebot/web/TMDbClientTest.java b/test/net/filebot/web/TMDbClientTest.java index 942da53e..0b97ffbc 100644 --- a/test/net/filebot/web/TMDbClientTest.java +++ b/test/net/filebot/web/TMDbClientTest.java @@ -20,7 +20,7 @@ import net.filebot.web.TMDbClient.MovieInfo; public class TMDbClientTest { - static TMDbClient tmdb = new TMDbClient("66308fb6e3fd850dde4c7d21df2e8306"); + TMDbClient tmdb = new TMDbClient("66308fb6e3fd850dde4c7d21df2e8306"); @Test public void searchByName() throws Exception { @@ -94,62 +94,6 @@ public class TMDbClientTest { assertEquals("https://image.tmdb.org/t/p/original/ac0HwGJIU3GxjjGujlIjLJmAGPR.jpg", artwork.get(0).getUrl().toString()); } - SearchResult buffy = new SearchResult(95, "Buffy the Vampire Slayer"); - SearchResult wonderfalls = new SearchResult(1982, "Wonderfalls"); - SearchResult firefly = new SearchResult(1437, "Firefly"); - - @Test - public void search() throws Exception { - // test default language and query escaping (blanks) - List results = tmdb.search("babylon 5", Locale.ENGLISH); - - assertEquals(1, results.size()); - - assertEquals("Babylon 5", results.get(0).getName()); - assertEquals(3137, results.get(0).getId()); - } - - @Test - public void getEpisodeListAll() throws Exception { - List list = tmdb.getEpisodeList(buffy, SortOrder.Airdate, Locale.ENGLISH); - - assertTrue(list.size() >= 144); - - // check ordinary episode - Episode first = list.get(0); - assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); - assertEquals("1997-03-10", first.getSeriesInfo().getStartDate().toString()); - assertEquals("Welcome to the Hellmouth (1)", first.getTitle()); - assertEquals("1", first.getEpisode().toString()); - assertEquals("1", first.getSeason().toString()); - assertEquals("1", first.getAbsolute().toString()); - assertEquals("1997-03-10", first.getAirdate().toString()); - - // check special episode - Episode last = list.get(list.size() - 1); - assertEquals("Buffy the Vampire Slayer", last.getSeriesName()); - assertEquals("Unaired Buffy the Vampire Slayer pilot", last.getTitle()); - assertEquals(null, last.getSeason()); - assertEquals(null, last.getEpisode()); - assertEquals(null, last.getAbsolute()); - assertEquals("1", last.getSpecial().toString()); - assertEquals(null, last.getAirdate()); - } - - @Test - public void getEpisodeListSingleSeason() throws Exception { - List list = tmdb.getEpisodeList(wonderfalls, SortOrder.Airdate, Locale.ENGLISH); - - Episode first = list.get(0); - assertEquals("Wonderfalls", first.getSeriesName()); - assertEquals("2004-03-12", first.getSeriesInfo().getStartDate().toString()); - assertEquals("Wax Lion", first.getTitle()); - assertEquals("1", first.getEpisode().toString()); - assertEquals("1", first.getSeason().toString()); - assertEquals("1", first.getAbsolute().toString()); - assertEquals("2004-03-12", first.getAirdate().toString()); - } - @Ignore @Test public void floodLimit() throws Exception { diff --git a/test/net/filebot/web/TMDbTVClientTest.java b/test/net/filebot/web/TMDbTVClientTest.java new file mode 100644 index 00000000..a77a6211 --- /dev/null +++ b/test/net/filebot/web/TMDbTVClientTest.java @@ -0,0 +1,70 @@ +package net.filebot.web; + +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Locale; + +import org.junit.Test; + +public class TMDbTVClientTest { + + TMDbTVClient tmdb = new TMDbTVClient(new TMDbClient("66308fb6e3fd850dde4c7d21df2e8306")); + + SearchResult buffy = new SearchResult(95, "Buffy the Vampire Slayer"); + SearchResult wonderfalls = new SearchResult(1982, "Wonderfalls"); + SearchResult firefly = new SearchResult(1437, "Firefly"); + + @Test + public void search() throws Exception { + // test default language and query escaping (blanks) + List results = tmdb.search("babylon 5", Locale.ENGLISH); + + assertEquals(1, results.size()); + + assertEquals("Babylon 5", results.get(0).getName()); + assertEquals(3137, results.get(0).getId()); + } + + @Test + public void getEpisodeListAll() throws Exception { + List list = tmdb.getEpisodeList(buffy, SortOrder.Airdate, Locale.ENGLISH); + + assertTrue(list.size() >= 144); + + // check ordinary episode + Episode first = list.get(0); + assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); + assertEquals("1997-03-10", first.getSeriesInfo().getStartDate().toString()); + assertEquals("Welcome to the Hellmouth (1)", first.getTitle()); + assertEquals("1", first.getEpisode().toString()); + assertEquals("1", first.getSeason().toString()); + assertEquals("1", first.getAbsolute().toString()); + assertEquals("1997-03-10", first.getAirdate().toString()); + + // check special episode + Episode last = list.get(list.size() - 1); + assertEquals("Buffy the Vampire Slayer", last.getSeriesName()); + assertEquals("Unaired Buffy the Vampire Slayer pilot", last.getTitle()); + assertEquals(null, last.getSeason()); + assertEquals(null, last.getEpisode()); + assertEquals(null, last.getAbsolute()); + assertEquals("1", last.getSpecial().toString()); + assertEquals(null, last.getAirdate()); + } + + @Test + public void getEpisodeListSingleSeason() throws Exception { + List list = tmdb.getEpisodeList(wonderfalls, SortOrder.Airdate, Locale.ENGLISH); + + Episode first = list.get(0); + assertEquals("Wonderfalls", first.getSeriesName()); + assertEquals("2004-03-12", first.getSeriesInfo().getStartDate().toString()); + assertEquals("Wax Lion", first.getTitle()); + assertEquals("1", first.getEpisode().toString()); + assertEquals("1", first.getSeason().toString()); + assertEquals("1", first.getAbsolute().toString()); + assertEquals("2004-03-12", first.getAirdate().toString()); + } + +} diff --git a/test/net/filebot/web/WebTestSuite.java b/test/net/filebot/web/WebTestSuite.java index 04715135..f07e55ff 100644 --- a/test/net/filebot/web/WebTestSuite.java +++ b/test/net/filebot/web/WebTestSuite.java @@ -5,7 +5,7 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ SimpleDateTest.class, AnidbClientTest.class, TheTVDBClientTest.class, TVMazeClientTest.class, TMDbClientTest.class, OMDbClientTest.class, OpenSubtitlesXmlRpcTest.class, AcoustIDClientTest.class }) +@SuiteClasses({ SimpleDateTest.class, AnidbClientTest.class, TheTVDBClientTest.class, TVMazeClientTest.class, TMDbClientTest.class, TMDbTVClientTest.class, OMDbClientTest.class, OpenSubtitlesXmlRpcTest.class, AcoustIDClientTest.class }) public class WebTestSuite { }