From ec548473904db66763ecff9953a834a427efa425 Mon Sep 17 00:00:00 2001 From: Conor Egan <68134729+c-eg@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:21:58 +0000 Subject: [PATCH] Replace web browser with okhttp3 client (#155) * Make all functions that call the api throw an exception if it fails * Better error handling * Replace WebBrowser with an OkHttp3 Client * Can pass api key in header instead of storing in TmdbApi * Better performance and overall a better way to go about it * Add ObjectReader to AbstractTmdbApi for better performance when reading ResponseStatus and checking for errors * Add TmdbResponseCode enum for tmdb responses --- build.gradle | 2 + config/checkstyle/suppressions.xml | 2 + .../movito/themoviedbapi/AbstractTmdbApi.java | 74 ++-- .../movito/themoviedbapi/TmdbAccount.java | 59 +-- .../info/movito/themoviedbapi/TmdbApi.java | 75 +--- .../themoviedbapi/TmdbAuthentication.java | 11 +- .../movito/themoviedbapi/TmdbCollections.java | 5 +- .../movito/themoviedbapi/TmdbCompany.java | 5 +- .../info/movito/themoviedbapi/TmdbConfig.java | 3 +- .../movito/themoviedbapi/TmdbDiscover.java | 10 +- .../info/movito/themoviedbapi/TmdbFind.java | 3 +- .../info/movito/themoviedbapi/TmdbGenre.java | 10 +- .../movito/themoviedbapi/TmdbKeywords.java | 5 +- .../info/movito/themoviedbapi/TmdbLists.java | 27 +- .../info/movito/themoviedbapi/TmdbMovies.java | 39 +- .../info/movito/themoviedbapi/TmdbPeople.java | 11 +- .../movito/themoviedbapi/TmdbReviews.java | 3 +- .../info/movito/themoviedbapi/TmdbSearch.java | 18 +- .../info/movito/themoviedbapi/TmdbTV.java | 19 +- .../movito/themoviedbapi/TmdbTimezones.java | 7 +- .../movito/themoviedbapi/TmdbTvEpisodes.java | 5 +- .../movito/themoviedbapi/TmdbTvSeasons.java | 4 +- .../java/info/movito/themoviedbapi/Utils.java | 5 +- .../model/MovieListCreationStatus.java | 2 +- .../model/core/ResponseStatusException.java | 23 -- .../core/{ => responses}/ResponseStatus.java | 10 +- .../ResponseStatusAuthentication.java | 12 + .../core/responses/ResponseStatusDelete.java | 13 + .../core/responses/TmdbResponseException.java | 27 ++ .../themoviedbapi/tools/RequestType.java | 1 + .../themoviedbapi/tools/TmdbException.java | 18 + .../themoviedbapi/tools/TmdbHttpClient.java | 49 +++ .../themoviedbapi/tools/TmdbResponseCode.java | 121 ++++++ .../themoviedbapi/tools/TmdbUrlReader.java | 21 ++ .../movito/themoviedbapi/tools/UrlReader.java | 13 - .../themoviedbapi/tools/WebBrowser.java | 349 ------------------ src/main/java/module-info.java | 3 + 37 files changed, 462 insertions(+), 602 deletions(-) delete mode 100644 src/main/java/info/movito/themoviedbapi/model/core/ResponseStatusException.java rename src/main/java/info/movito/themoviedbapi/model/core/{ => responses}/ResponseStatus.java (60%) create mode 100644 src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusAuthentication.java create mode 100644 src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusDelete.java create mode 100644 src/main/java/info/movito/themoviedbapi/model/core/responses/TmdbResponseException.java create mode 100644 src/main/java/info/movito/themoviedbapi/tools/TmdbException.java create mode 100644 src/main/java/info/movito/themoviedbapi/tools/TmdbHttpClient.java create mode 100644 src/main/java/info/movito/themoviedbapi/tools/TmdbResponseCode.java create mode 100644 src/main/java/info/movito/themoviedbapi/tools/TmdbUrlReader.java delete mode 100644 src/main/java/info/movito/themoviedbapi/tools/UrlReader.java delete mode 100644 src/main/java/info/movito/themoviedbapi/tools/WebBrowser.java diff --git a/build.gradle b/build.gradle index a6ae0184..43b07f33 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,8 @@ dependencies { testCompileOnly 'org.projectlombok:lombok:1.18.30' testAnnotationProcessor 'org.projectlombok:lombok:1.18.30' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.16.0' implementation 'com.fasterxml.jackson.core:jackson-core:2.16.0' implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.0' diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index eff3af60..1d9dde25 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -8,4 +8,6 @@ + + diff --git a/src/main/java/info/movito/themoviedbapi/AbstractTmdbApi.java b/src/main/java/info/movito/themoviedbapi/AbstractTmdbApi.java index ad9105a6..1a8c5712 100644 --- a/src/main/java/info/movito/themoviedbapi/AbstractTmdbApi.java +++ b/src/main/java/info/movito/themoviedbapi/AbstractTmdbApi.java @@ -1,18 +1,20 @@ package info.movito.themoviedbapi; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import info.movito.themoviedbapi.model.core.ResponseStatus; -import info.movito.themoviedbapi.model.core.ResponseStatusException; +import com.fasterxml.jackson.databind.ObjectReader; +import info.movito.themoviedbapi.model.core.responses.ResponseStatus; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; -import info.movito.themoviedbapi.tools.MovieDbException; import info.movito.themoviedbapi.tools.RequestType; +import info.movito.themoviedbapi.tools.TmdbResponseCode; +import lombok.AccessLevel; +import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; +import static info.movito.themoviedbapi.tools.TmdbResponseCode.REQUEST_LIMIT_EXCEEDED; /** * Class to be inherited by a TmdbApi class. @@ -30,14 +32,11 @@ public abstract class AbstractTmdbApi { public static final String PARAM_API_KEY = "api_key"; - protected static final ObjectMapper jsonMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + @Getter(AccessLevel.PROTECTED) + private static final ObjectMapper objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - // see https://www.themoviedb.org/documentation/api/status-codes - private static final Collection SUCCESS_STATUS_CODES = Arrays.asList( - 1, // Success - 12, // The item/record was updated successfully. - 13 // The item/record was updated successfully. - ); + private static final ObjectReader responseStatusReader = objectMapper.readerFor(ResponseStatus.class); protected final TmdbApi tmdbApi; @@ -55,55 +54,60 @@ public abstract class AbstractTmdbApi { * @param the type of class to map to * @return the mapped class */ - public T mapJsonResult(ApiUrl apiUrl, Class clazz) { - return mapJsonResult(apiUrl, clazz, null); + public T mapJsonResult(ApiUrl apiUrl, Class clazz) throws TmdbResponseException { + return mapJsonResult(apiUrl, null, clazz); } /** * Maps a json result to a class. * * @param apiUrl the api url to map - * @param clazz the class to map to * @param jsonBody the json body + * @param clazz the class to map to * @param the type of class to map to * @return the mapped class. */ - public T mapJsonResult(ApiUrl apiUrl, Class clazz, String jsonBody) { - return mapJsonResult(apiUrl, clazz, jsonBody, RequestType.GET); + public T mapJsonResult(ApiUrl apiUrl, String jsonBody, Class clazz) throws TmdbResponseException { + return mapJsonResult(apiUrl, jsonBody, RequestType.GET, clazz); } /** * Maps a json result to a class. * * @param apiUrl the api url to map - * @param clazz the class to map to * @param jsonBody the json body * @param requestType the type of request + * @param clazz the class to map to * @param the type of class to map to * @return the mapped class. */ - public T mapJsonResult(ApiUrl apiUrl, Class clazz, String jsonBody, RequestType requestType) { - String webpage = tmdbApi.requestWebPage(apiUrl, jsonBody, requestType); + public T mapJsonResult(ApiUrl apiUrl, String jsonBody, RequestType requestType, Class clazz) throws TmdbResponseException { + String jsonResponse = tmdbApi.getTmdbUrlReader().readUrl(apiUrl.buildUrl(), jsonBody, requestType); try { - // check if was error responseStatus - ResponseStatus responseStatus = jsonMapper.readValue(webpage, ResponseStatus.class); - // work around the problem that there's no status code for suspected spam names yet - String suspectedSpam = "Unable to create list because: Description is suspected to be spam."; - if (webpage.contains(suspectedSpam)) { - responseStatus = new ResponseStatus(-100, suspectedSpam); - } - - // if null, the json response was not a error responseStatus code, but something else + // check if the response was successful. tmdb have their own codes for successful and unsuccessful responses. + // some 2xx codes are not successful. See: https://developer.themoviedb.org/docs/errors for more info. + ResponseStatus responseStatus = responseStatusReader.readValue(jsonResponse); Integer statusCode = responseStatus.getStatusCode(); - if (statusCode != null && !SUCCESS_STATUS_CODES.contains(statusCode)) { - throw new ResponseStatusException(responseStatus); + + if (statusCode != null) { + TmdbResponseCode tmdbResponseCode = TmdbResponseCode.fromCode(statusCode); + + if (tmdbResponseCode != null) { + if (REQUEST_LIMIT_EXCEEDED == tmdbResponseCode) { + Thread.sleep(1000); + return mapJsonResult(apiUrl, jsonBody, requestType, clazz); + } + else if (!tmdbResponseCode.isSuccess()) { + throw new TmdbResponseException(tmdbResponseCode); + } + } } - return jsonMapper.readValue(webpage, clazz); + return objectMapper.readValue(jsonResponse, clazz); } - catch (IOException ex) { - throw new MovieDbException("mapping failed:\n" + webpage, ex); + catch (JsonProcessingException | InterruptedException exception) { + throw new TmdbResponseException(exception); } } } diff --git a/src/main/java/info/movito/themoviedbapi/TmdbAccount.java b/src/main/java/info/movito/themoviedbapi/TmdbAccount.java index db4bccff..134710d4 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbAccount.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbAccount.java @@ -4,11 +4,13 @@ import info.movito.themoviedbapi.model.config.Account; import info.movito.themoviedbapi.model.core.AccountID; import info.movito.themoviedbapi.model.core.MovieResultsPage; -import info.movito.themoviedbapi.model.core.ResponseStatus; +import info.movito.themoviedbapi.model.core.responses.ResponseStatus; import info.movito.themoviedbapi.model.core.ResultsPage; import info.movito.themoviedbapi.model.core.SessionToken; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import info.movito.themoviedbapi.tools.MovieDbException; +import info.movito.themoviedbapi.tools.TmdbResponseCode; import java.util.Collections; import java.util.HashMap; @@ -36,7 +38,7 @@ public class TmdbAccount extends AbstractTmdbApi { /** * Get the basic information for an account. You will need to have a valid session id. */ - public Account getAccount(SessionToken sessionToken) { + public Account getAccount(SessionToken sessionToken) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -48,7 +50,7 @@ public Account getAccount(SessionToken sessionToken) { * Get the lists that as user has created. */ public MovieListResultsPage getLists(SessionToken sessionToken, AccountID accountId, String language, - Integer page) { + Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "lists"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -61,7 +63,7 @@ public MovieListResultsPage getLists(SessionToken sessionToken, AccountID accoun /** * Get the rated movies from the account. */ - public MovieResultsPage getRatedMovies(SessionToken sessionToken, AccountID accountId, Integer page) { + public MovieResultsPage getRatedMovies(SessionToken sessionToken, AccountID accountId, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "rated/movies"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -73,7 +75,7 @@ public MovieResultsPage getRatedMovies(SessionToken sessionToken, AccountID acco /** * Get the rated tv shows from the account. */ - public TvResultsPage getRatedTvSeries(SessionToken sessionToken, AccountID accountId, Integer page) { + public TvResultsPage getRatedTvSeries(SessionToken sessionToken, AccountID accountId, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "rated/tv"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -85,7 +87,8 @@ public TvResultsPage getRatedTvSeries(SessionToken sessionToken, AccountID accou /** * Get the rated tv episodes from the account. */ - public TvEpisodesResultsPage getRatedEpisodes(SessionToken sessionToken, AccountID accountId, Integer page) { + public TvEpisodesResultsPage getRatedEpisodes(SessionToken sessionToken, AccountID accountId, Integer page) + throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "rated/tv/episodes"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -99,7 +102,7 @@ public TvEpisodesResultsPage getRatedEpisodes(SessionToken sessionToken, Account *

* A valid session id is required. */ - public boolean postMovieRating(SessionToken sessionToken, Integer movieId, Integer rating) { + public boolean postMovieRating(SessionToken sessionToken, Integer movieId, Integer rating) throws TmdbResponseException { return postRatingInternal(sessionToken, rating, new ApiUrl(TmdbMovies.TMDB_METHOD_MOVIE, movieId, "rating")); } @@ -108,7 +111,7 @@ public boolean postMovieRating(SessionToken sessionToken, Integer movieId, Integ *

* A valid session id is required. */ - public boolean postTvSeriesRating(SessionToken sessionToken, Integer movieId, Integer rating) { + public boolean postTvSeriesRating(SessionToken sessionToken, Integer movieId, Integer rating) throws TmdbResponseException { return postRatingInternal(sessionToken, rating, new ApiUrl(TmdbTV.TMDB_METHOD_TV, movieId, "rating")); } @@ -116,7 +119,7 @@ public boolean postTvSeriesRating(SessionToken sessionToken, Integer movieId, In * This method lets users rate a tv episode. */ public boolean postTvExpisodeRating(SessionToken sessionToken, Integer seriesId, Integer seasonNumber, - Integer episodeNumber, Integer rating) { + Integer episodeNumber, Integer rating) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl( TMDB_METHOD_TV, seriesId, TMDB_METHOD_TV_SEASON, seasonNumber, @@ -127,22 +130,22 @@ public boolean postTvExpisodeRating(SessionToken sessionToken, Integer seriesId, return postRatingInternal(sessionToken, rating, apiUrl); } - private boolean postRatingInternal(SessionToken sessionToken, Integer rating, ApiUrl apiUrl) { + private boolean postRatingInternal(SessionToken sessionToken, Integer rating, ApiUrl apiUrl) throws TmdbResponseException { apiUrl.addPathParam(PARAM_SESSION, sessionToken); if (rating < 0 || rating > 10) { throw new MovieDbException("rating out of range"); } - String jsonBody = Utils.convertToJson(jsonMapper, Collections.singletonMap("value", rating)); + String jsonBody = Utils.convertToJson(getObjectMapper(), Collections.singletonMap("value", rating)); - return mapJsonResult(apiUrl, ResponseStatus.class, jsonBody).getStatusCode() == 12; + return mapJsonResult(apiUrl, jsonBody, ResponseStatus.class).getStatusCode() == TmdbResponseCode.ITEM_UPDATED.getTmdbCode(); } /** * Get favourites movies from the account. */ - public MovieResultsPage getFavoriteMovies(SessionToken sessionToken, AccountID accountId) { + public MovieResultsPage getFavoriteMovies(SessionToken sessionToken, AccountID accountId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "favorite/movies"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -152,7 +155,7 @@ public MovieResultsPage getFavoriteMovies(SessionToken sessionToken, AccountID a /** * Get the favorite tv shows from the account. */ - public TvResultsPage getFavoriteSeries(SessionToken sessionToken, AccountID accountId, Integer page) { + public TvResultsPage getFavoriteSeries(SessionToken sessionToken, AccountID accountId, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "favorite/tv"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -165,7 +168,7 @@ public TvResultsPage getFavoriteSeries(SessionToken sessionToken, AccountID acco * Remove a movie from an account's favorites list. */ public ResponseStatus addFavorite(SessionToken sessionToken, AccountID accountId, Integer movieId, - MediaType mediaType) { + MediaType mediaType) throws TmdbResponseException { return changeFavoriteStatus(sessionToken, accountId, movieId, mediaType, true); } @@ -173,25 +176,25 @@ public ResponseStatus addFavorite(SessionToken sessionToken, AccountID accountId * Remove a movie from an account's favorites list. */ public ResponseStatus removeFavorite(SessionToken sessionToken, AccountID accountId, Integer movieId, - MediaType mediaType) { + MediaType mediaType) throws TmdbResponseException { return changeFavoriteStatus(sessionToken, accountId, movieId, mediaType, false); } private ResponseStatus changeFavoriteStatus(SessionToken sessionToken, AccountID accountId, Integer movieId, - MediaType mediaType, boolean isFavorite) { + MediaType mediaType, boolean isFavorite) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "favorite"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); - HashMap body = new HashMap(); + HashMap body = new HashMap<>(); body.put("media_type", mediaType.toString()); body.put("media_id", movieId); body.put("favorite", isFavorite); - String jsonBody = Utils.convertToJson(jsonMapper, body); + String jsonBody = Utils.convertToJson(getObjectMapper(), body); - return mapJsonResult(apiUrl, ResponseStatus.class, jsonBody); + return mapJsonResult(apiUrl, jsonBody, ResponseStatus.class); } /** @@ -199,7 +202,7 @@ private ResponseStatus changeFavoriteStatus(SessionToken sessionToken, AccountID * * @return The watchlist of the user */ - public MovieResultsPage getWatchListMovies(SessionToken sessionToken, AccountID accountId, Integer page) { + public MovieResultsPage getWatchListMovies(SessionToken sessionToken, AccountID accountId, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "watchlist/movies"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -213,7 +216,7 @@ public MovieResultsPage getWatchListMovies(SessionToken sessionToken, AccountID * * @return The watchlist of the user */ - public TvResultsPage getWatchListSeries(SessionToken sessionToken, AccountID accountId, Integer page) { + public TvResultsPage getWatchListSeries(SessionToken sessionToken, AccountID accountId, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "watchlist/tv"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); @@ -226,7 +229,7 @@ public TvResultsPage getWatchListSeries(SessionToken sessionToken, AccountID acc * Add a movie to an account's watch list. */ public ResponseStatus addToWatchList(SessionToken sessionToken, AccountID accountId, Integer movieId, - MediaType mediaType) { + MediaType mediaType) throws TmdbResponseException { return modifyWatchList(sessionToken, accountId, movieId, mediaType, true); } @@ -234,25 +237,25 @@ public ResponseStatus addToWatchList(SessionToken sessionToken, AccountID accoun * Remove a movie from an account's watch list. */ public ResponseStatus removeFromWatchList(SessionToken sessionToken, AccountID accountId, Integer movieId, - MediaType mediaType) { + MediaType mediaType) throws TmdbResponseException { return modifyWatchList(sessionToken, accountId, movieId, mediaType, false); } private ResponseStatus modifyWatchList(SessionToken sessionToken, AccountID accountId, Integer movieId, - MediaType mediaType, boolean isWatched) { + MediaType mediaType, boolean isWatched) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_ACCOUNT, accountId, "watchlist"); apiUrl.addPathParam(PARAM_SESSION, sessionToken); - HashMap body = new HashMap(); + HashMap body = new HashMap<>(); body.put("media_type", mediaType.toString()); body.put("media_id", movieId); body.put("watchlist", isWatched); - String jsonBody = Utils.convertToJson(jsonMapper, body); + String jsonBody = Utils.convertToJson(getObjectMapper(), body); - return mapJsonResult(apiUrl, ResponseStatus.class, jsonBody); + return mapJsonResult(apiUrl, jsonBody, ResponseStatus.class); } /** diff --git a/src/main/java/info/movito/themoviedbapi/TmdbApi.java b/src/main/java/info/movito/themoviedbapi/TmdbApi.java index e7cf125a..ffc9a12d 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbApi.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbApi.java @@ -2,16 +2,13 @@ import info.movito.themoviedbapi.model.config.Timezone; import info.movito.themoviedbapi.model.config.TmdbConfiguration; -import info.movito.themoviedbapi.tools.ApiUrl; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.MovieDbException; -import info.movito.themoviedbapi.tools.RequestCountLimitException; -import info.movito.themoviedbapi.tools.RequestType; -import info.movito.themoviedbapi.tools.UrlReader; -import info.movito.themoviedbapi.tools.WebBrowser; +import info.movito.themoviedbapi.tools.TmdbHttpClient; +import info.movito.themoviedbapi.tools.TmdbUrlReader; +import lombok.AccessLevel; import lombok.Getter; -import org.apache.commons.lang3.StringUtils; -import java.net.URL; import java.util.List; /** @@ -21,21 +18,14 @@ * @author Holger Brandl. */ public class TmdbApi { - @Getter - private final String apiKey; - - private final TmdbConfiguration tmdbConfig; - /** - * Reader implementation that is used to fetch all websites. + * Http client to make requests to the movie database api. + * can make certain things static if necessary */ - private final UrlReader urlReader; + @Getter(AccessLevel.PROTECTED) + private final TmdbUrlReader tmdbUrlReader; - /** - * Automatically retry after indicated amount of seconds if we hit the request limit. See the - * documentation for details. - */ - private final boolean autoRetry; + private final TmdbConfiguration tmdbConfig; /** * Constructor. @@ -43,20 +33,16 @@ public class TmdbApi { * @param apiKey your TMDB api key */ public TmdbApi(String apiKey) { - this(apiKey, new WebBrowser(), true); + this(new TmdbHttpClient(apiKey)); } /** * Constructor. * - * @param apiKey your TMDB api key - * @param urlReader the reader implementation that is used to fetch all websites - * @param autoRetry automatically retry after indicated amount of seconds if we hit the request limit. + * @param tmdbUrlReader the url reader to use */ - public TmdbApi(String apiKey, UrlReader urlReader, boolean autoRetry) { - this.urlReader = urlReader; - this.apiKey = apiKey; - this.autoRetry = autoRetry; + public TmdbApi(TmdbUrlReader tmdbUrlReader) { + this.tmdbUrlReader = tmdbUrlReader; try { tmdbConfig = new TmdbConfig(this).getConfig().getTmdbConfiguration(); @@ -69,43 +55,12 @@ public TmdbApi(String apiKey, UrlReader urlReader, boolean autoRetry) { } } - /** - * Uses the instance's api key to request information from api.tmdb.org. - * - * Depending on the autoRetry setting this method will stall and internally recurse until the request was successfully - * processed. - * - * @param apiUrl The url to be requested - * @param jsonBody can be null - */ - public String requestWebPage(ApiUrl apiUrl, String jsonBody, RequestType requestType) { - assert StringUtils.isNotBlank(apiKey); - apiUrl.addPathParam(AbstractTmdbApi.PARAM_API_KEY, getApiKey()); - - return requestWebPageInternal(apiUrl.buildUrl(), jsonBody, requestType); - } - - private String requestWebPageInternal(URL url, String jsonBody, RequestType requestType) { - try { - return urlReader.request(url, jsonBody, requestType); - } - catch (RequestCountLimitException rcle) { - if (autoRetry) { - Utils.sleep(rcle.getRetryAfter() * 1000); - return requestWebPageInternal(url, jsonBody, requestType); - } - else { - // just return the orignal json response if autoRetry is disabled. This will cause a ResponseStatusException. - return rcle.getMessage(); - } - } - } - public TmdbConfiguration getConfiguration() { return tmdbConfig; } - public List getTimezones() { + @SuppressWarnings("checkstyle:MissingJavadocMethod") + public List getTimezones() throws TmdbResponseException { return new TmdbTimezones(this).getTimezones(); } diff --git a/src/main/java/info/movito/themoviedbapi/TmdbAuthentication.java b/src/main/java/info/movito/themoviedbapi/TmdbAuthentication.java index 1f578a4f..0566d6c9 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbAuthentication.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbAuthentication.java @@ -2,6 +2,7 @@ import info.movito.themoviedbapi.model.config.TokenAuthorisation; import info.movito.themoviedbapi.model.config.TokenSession; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import info.movito.themoviedbapi.tools.MovieDbException; @@ -31,7 +32,7 @@ public class TmdbAuthentication extends AbstractTmdbApi { * As soon as a valid session id has been created the token will be destroyed. */ - public TokenAuthorisation getAuthorisationToken() { + public TokenAuthorisation getAuthorisationToken() throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_AUTH, "token/new"); return mapJsonResult(apiUrl, TokenAuthorisation.class); @@ -42,7 +43,7 @@ public TokenAuthorisation getAuthorisationToken() { * * A session id is required in order to use any of the write methods. */ - public TokenSession getSessionToken(TokenAuthorisation token) { + public TokenSession getSessionToken(TokenAuthorisation token) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_AUTH, "session/new"); if (!token.getSuccess()) { @@ -63,7 +64,7 @@ public TokenSession getSessionToken(TokenAuthorisation token) { * @param pwd password * @return The validated TokenAuthorisation. The same as input with getSuccess()==true */ - public TokenAuthorisation getLoginToken(TokenAuthorisation token, String user, String pwd) { + public TokenAuthorisation getLoginToken(TokenAuthorisation token, String user, String pwd) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_AUTH, "token/validate_with_login"); apiUrl.addPathParam(PARAM_REQUEST_TOKEN, token.getRequestToken()); @@ -88,7 +89,7 @@ public TokenAuthorisation getLoginToken(TokenAuthorisation token, String user, S * @return validated TokenSession * @throws info.movito.themoviedbapi.tools.MovieDbException if the login failed */ - public TokenSession getSessionLogin(String username, String password) { + public TokenSession getSessionLogin(String username, String password) throws TmdbResponseException { TokenAuthorisation authToken = getAuthorisationToken(); if (!authToken.getSuccess()) { @@ -117,7 +118,7 @@ public TokenSession getSessionLogin(String username, String password) { * * If a guest session is not used for the first time within 24 hours, it will be automatically discarded. */ - public TokenSession getGuestSessionToken() { + public TokenSession getGuestSessionToken() throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_AUTH, "guest_session/new"); return mapJsonResult(apiUrl, TokenSession.class); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbCollections.java b/src/main/java/info/movito/themoviedbapi/TmdbCollections.java index 684d3e70..fb17df89 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbCollections.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbCollections.java @@ -3,6 +3,7 @@ import info.movito.themoviedbapi.model.Artwork; import info.movito.themoviedbapi.model.CollectionInfo; import info.movito.themoviedbapi.model.MovieImages; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import java.util.List; @@ -29,7 +30,7 @@ public class TmdbCollections extends AbstractTmdbApi { * * You can get the ID needed for this method by making a getMovieInfo request for the belongs_to_collection. */ - public CollectionInfo getCollectionInfo(int collectionId, String language) { + public CollectionInfo getCollectionInfo(int collectionId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_COLLECTION, collectionId); apiUrl.addLanguage(language); @@ -40,7 +41,7 @@ public CollectionInfo getCollectionInfo(int collectionId, String language) { /** * Get all of the images for a particular collection by collection id. */ - public List getCollectionImages(int collectionId, String language) { + public List getCollectionImages(int collectionId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_COLLECTION, collectionId, "images"); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbCompany.java b/src/main/java/info/movito/themoviedbapi/TmdbCompany.java index a4f77472..77e48b5b 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbCompany.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbCompany.java @@ -3,6 +3,7 @@ import info.movito.themoviedbapi.model.Collection; import info.movito.themoviedbapi.model.Company; import info.movito.themoviedbapi.model.core.ResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; /** @@ -22,7 +23,7 @@ public class TmdbCompany extends AbstractTmdbApi { /** * This method is used to retrieve the basic information about a production company on TMDb. */ - public Company getCompanyInfo(int companyId) { + public Company getCompanyInfo(int companyId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_COMPANY, companyId); return mapJsonResult(apiUrl, Company.class); @@ -33,7 +34,7 @@ public Company getCompanyInfo(int companyId) { * * These movies are returned in order of most recently released to oldest. The default response will return 20 */ - public CollectionResultsPage getCompanyMovies(int companyId, String language, Integer page) { + public CollectionResultsPage getCompanyMovies(int companyId, String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_COMPANY, companyId, "movies"); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbConfig.java b/src/main/java/info/movito/themoviedbapi/TmdbConfig.java index ca976dfd..129d97ef 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbConfig.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbConfig.java @@ -1,6 +1,7 @@ package info.movito.themoviedbapi; import info.movito.themoviedbapi.model.config.ConfigResults; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; /** @@ -17,7 +18,7 @@ class TmdbConfig extends AbstractTmdbApi { super(tmdbApi); } - public ConfigResults getConfig() { + public ConfigResults getConfig() throws TmdbResponseException { return mapJsonResult(new ApiUrl(TMDB_METHOD_CONFIGURATION), ConfigResults.class); } } diff --git a/src/main/java/info/movito/themoviedbapi/TmdbDiscover.java b/src/main/java/info/movito/themoviedbapi/TmdbDiscover.java index 5e0c5a0f..c09b56d4 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbDiscover.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbDiscover.java @@ -2,6 +2,7 @@ import info.movito.themoviedbapi.model.Discover; import info.movito.themoviedbapi.model.core.MovieResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; /** @@ -50,7 +51,7 @@ public class TmdbDiscover extends AbstractTmdbApi { public MovieResultsPage getDiscover(int page, String language, String sortBy, boolean includeAdult, int year, int primaryReleaseYear, int voteCountGte, float voteAverageGte, String withGenres, String releaseDateGte, String releaseDateLte, String certificationCountry, String certificationLte, - String withCompanies) { + String withCompanies) throws TmdbResponseException { Discover discover = new Discover(); discover.page(page) @@ -77,7 +78,7 @@ public MovieResultsPage getDiscover(int page, String language, String sortBy, bo * @param discover A discover object containing the search criteria required * @return the movie results page. */ - public MovieResultsPage getDiscover(Discover discover) { + public MovieResultsPage getDiscover(Discover discover) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_DISCOVER, "movie"); for (String key : discover.getParams().keySet()) { @@ -118,7 +119,8 @@ public MovieResultsPage getDiscover(Discover discover) { */ public TvResultsPage getDiscoverTV(int page, String language, String sortBy, boolean includeAdult, int year, int primaryReleaseYear, int voteCountGte, float voteAverageGte, String withGenres, String releaseDateGte, - String releaseDateLte, String certificationCountry, String certificationLte, String withCompanies) { + String releaseDateLte, String certificationCountry, String certificationLte, String withCompanies) + throws TmdbResponseException { Discover discover = new Discover(); discover.page(page) @@ -145,7 +147,7 @@ public TvResultsPage getDiscoverTV(int page, String language, String sortBy, boo * @param discover A discover object containing the search criteria required * @return the tv results page. */ - public TvResultsPage getDiscoverTV(Discover discover) { + public TvResultsPage getDiscoverTV(Discover discover) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_DISCOVER, "tv"); for (String key : discover.getParams().keySet()) { diff --git a/src/main/java/info/movito/themoviedbapi/TmdbFind.java b/src/main/java/info/movito/themoviedbapi/TmdbFind.java index a80482c5..4e4fb018 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbFind.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbFind.java @@ -1,6 +1,7 @@ package info.movito.themoviedbapi; import info.movito.themoviedbapi.model.FindResults; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; /** @@ -20,7 +21,7 @@ public class TmdbFind extends AbstractTmdbApi { /** * Supported query ids are imdb, people, freebase, series. For details see http://docs.themoviedb.apiary.io/#find */ - public FindResults find(String id, ExternalSource externalSource, String language) { + public FindResults find(String id, ExternalSource externalSource, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_FIND, id); apiUrl.addPathParam("external_source", externalSource.toString()); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbGenre.java b/src/main/java/info/movito/themoviedbapi/TmdbGenre.java index b269fd42..01aaf2cd 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbGenre.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbGenre.java @@ -4,6 +4,7 @@ import info.movito.themoviedbapi.model.Genre; import info.movito.themoviedbapi.model.core.AbstractJsonMapping; import info.movito.themoviedbapi.model.core.MovieResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import java.util.List; @@ -31,7 +32,7 @@ public TmdbGenre(TmdbApi tmdbApi) { * @deprecated use {@code getMovieGenreList} as TV shows Genres was added. */ @Deprecated - public List getGenreList(String language) { + public List getGenreList(String language) throws TmdbResponseException { return getMovieGenreList(language); } @@ -41,7 +42,7 @@ public List getGenreList(String language) { * * These IDs will correspond to those found in movie calls. */ - public List getMovieGenreList(String language) { + public List getMovieGenreList(String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_GENRE, "movie", "list"); apiUrl.addLanguage(language); @@ -55,7 +56,7 @@ public List getMovieGenreList(String language) { * * These IDs will correspond to those found in TV shows calls. */ - public List getTvGenreList(String language) { + public List getTvGenreList(String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_GENRE, "tv", "list"); apiUrl.addLanguage(language); @@ -70,7 +71,8 @@ public List getTvGenreList(String language) { * * This prevents movies from 1 10/10 rating from being listed first and for the first 5 pages. */ - public MovieResultsPage getGenreMovies(int genreId, String language, Integer page, boolean includeAllMovies) { + public MovieResultsPage getGenreMovies(int genreId, String language, Integer page, boolean includeAllMovies) + throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_GENRE, genreId, "movies"); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbKeywords.java b/src/main/java/info/movito/themoviedbapi/TmdbKeywords.java index d987b46a..e1c39a6e 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbKeywords.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbKeywords.java @@ -2,6 +2,7 @@ import info.movito.themoviedbapi.model.core.MovieResultsPage; import info.movito.themoviedbapi.model.core.ResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.keywords.Keyword; import info.movito.themoviedbapi.tools.ApiUrl; @@ -22,7 +23,7 @@ public class TmdbKeywords extends AbstractTmdbApi { /** * Get the basic information for a specific keyword id. */ - public Keyword getKeyword(String keywordId) { + public Keyword getKeyword(String keywordId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_KEYWORD, keywordId); return mapJsonResult(apiUrl, Keyword.class); @@ -33,7 +34,7 @@ public Keyword getKeyword(String keywordId) { * * @return List of movies with the keyword */ - public MovieResultsPage getKeywordMovies(String keywordId, String language, Integer page) { + public MovieResultsPage getKeywordMovies(String keywordId, String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_KEYWORD, keywordId, "movies"); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbLists.java b/src/main/java/info/movito/themoviedbapi/TmdbLists.java index 70cf94d9..cbbba8fa 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbLists.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbLists.java @@ -3,8 +3,9 @@ import info.movito.themoviedbapi.model.ListItemStatus; import info.movito.themoviedbapi.model.MovieList; import info.movito.themoviedbapi.model.MovieListCreationStatus; -import info.movito.themoviedbapi.model.core.ResponseStatus; +import info.movito.themoviedbapi.model.core.responses.ResponseStatus; import info.movito.themoviedbapi.model.core.SessionToken; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import info.movito.themoviedbapi.tools.RequestType; import org.apache.commons.lang3.StringUtils; @@ -31,7 +32,7 @@ public TmdbLists(TmdbApi tmdbApi) { * * @return The list and its items */ - public MovieList getList(String listId) { + public MovieList getList(String listId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_LIST, listId); return mapJsonResult(apiUrl, MovieList.class); @@ -42,7 +43,7 @@ public MovieList getList(String listId) { * * @return The list id */ - public String createList(SessionToken sessionToken, String name, String description) { + public String createList(SessionToken sessionToken, String name, String description) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_LIST); apiUrl.addPathParam(TmdbAccount.PARAM_SESSION, sessionToken); @@ -51,9 +52,9 @@ public String createList(SessionToken sessionToken, String name, String descript body.put("name", StringUtils.trimToEmpty(name)); body.put("description", StringUtils.trimToEmpty(description)); - String jsonBody = Utils.convertToJson(jsonMapper, body); + String jsonBody = Utils.convertToJson(getObjectMapper(), body); - return mapJsonResult(apiUrl, MovieListCreationStatus.class, jsonBody).getListId(); + return mapJsonResult(apiUrl, jsonBody, MovieListCreationStatus.class).getListId(); } /** @@ -61,7 +62,7 @@ public String createList(SessionToken sessionToken, String name, String descript * * @return true if the movie is on the list */ - public boolean isMovieOnList(String listId, Integer movieId) { + public boolean isMovieOnList(String listId, Integer movieId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_LIST, listId, "item_status"); apiUrl.addPathParam("movie_id", movieId); @@ -74,7 +75,7 @@ public boolean isMovieOnList(String listId, Integer movieId) { * * @return true if the movie is on the list */ - public ResponseStatus addMovieToList(SessionToken sessionToken, String listId, Integer movieId) { + public ResponseStatus addMovieToList(SessionToken sessionToken, String listId, Integer movieId) throws TmdbResponseException { return modifyMovieList(sessionToken, listId, movieId, "add_item"); } @@ -83,29 +84,29 @@ public ResponseStatus addMovieToList(SessionToken sessionToken, String listId, I * * @return true if the movie is on the list */ - public ResponseStatus removeMovieFromList(SessionToken sessionToken, String listId, Integer movieId) { + public ResponseStatus removeMovieFromList(SessionToken sessionToken, String listId, Integer movieId) throws TmdbResponseException { return modifyMovieList(sessionToken, listId, movieId, "remove_item"); } private ResponseStatus modifyMovieList(SessionToken sessionToken, String listId, Integer movieId, - String operation) { + String operation) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_LIST, listId, operation); apiUrl.addPathParam(TmdbAccount.PARAM_SESSION, sessionToken); - String jsonBody = Utils.convertToJson(jsonMapper, Collections.singletonMap("media_id", movieId + "")); + String jsonBody = Utils.convertToJson(getObjectMapper(), Collections.singletonMap("media_id", movieId + "")); - return mapJsonResult(apiUrl, ResponseStatus.class, jsonBody); + return mapJsonResult(apiUrl, jsonBody, ResponseStatus.class); } /** * This method lets users delete a list that they created. A valid session id is required. */ - public ResponseStatus deleteMovieList(SessionToken sessionToken, String listId) { + public ResponseStatus deleteMovieList(SessionToken sessionToken, String listId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_LIST, listId); apiUrl.addPathParam(TmdbAccount.PARAM_SESSION, sessionToken); - return mapJsonResult(apiUrl, ResponseStatus.class, null, RequestType.DELETE); + return mapJsonResult(apiUrl, null, RequestType.DELETE, ResponseStatus.class); } } diff --git a/src/main/java/info/movito/themoviedbapi/TmdbMovies.java b/src/main/java/info/movito/themoviedbapi/TmdbMovies.java index 0790bfaf..97866a7b 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbMovies.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbMovies.java @@ -15,6 +15,7 @@ import info.movito.themoviedbapi.model.core.IdElement; import info.movito.themoviedbapi.model.core.MovieResultsPage; import info.movito.themoviedbapi.model.core.SessionToken; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.keywords.Keyword; import info.movito.themoviedbapi.model.providers.ProviderResults; import info.movito.themoviedbapi.tools.ApiUrl; @@ -55,7 +56,7 @@ public TmdbMovies(TmdbApi tmdbApi) { * * It will return the single highest rated poster and backdrop. */ - public MovieDb getMovie(int movieId, String language, MovieMethod... appendToResponse) { + public MovieDb getMovie(int movieId, String language, MovieMethod... appendToResponse) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId); apiUrl.addLanguage(language); @@ -68,7 +69,7 @@ public MovieDb getMovie(int movieId, String language, MovieMethod... appendToRes /** * This method is used to retrieve all the alternative titles we have for a particular movie. */ - public List getAlternativeTitles(int movieId, String country) { + public List getAlternativeTitles(int movieId, String country) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId, MovieMethod.alternative_titles); if (StringUtils.isNotBlank(country)) { @@ -84,7 +85,7 @@ public List getAlternativeTitles(int movieId, String country) * @param movieId the movies id * @return the movie credits */ - public Credits getCredits(int movieId) { + public Credits getCredits(int movieId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId, MovieMethod.credits); return mapJsonResult(apiUrl, Credits.class); @@ -93,7 +94,7 @@ public Credits getCredits(int movieId) { /** * This method should be used when you’re wanting to retrieve all of the images for a particular movie. */ - public MovieImages getImages(int movieId, String language) { + public MovieImages getImages(int movieId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId, MovieMethod.images); apiUrl.addLanguage(language); @@ -106,7 +107,7 @@ public MovieImages getImages(int movieId, String language) { * * Currently, only English keywords exist. */ - public List getKeywords(int movieId) { + public List getKeywords(int movieId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId, MovieMethod.keywords); return mapJsonResult(apiUrl, KeywordResults.class).results; @@ -115,7 +116,7 @@ public List getKeywords(int movieId) { /** * This method is used to retrieve all of the release and certification data we have for a specific movie. */ - public List getReleaseInfo(int movieId, String language) { + public List getReleaseInfo(int movieId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId, MovieMethod.release_dates); apiUrl.addLanguage(language); @@ -128,7 +129,7 @@ public List getReleaseInfo(int movieId, String language) { * * Supported sites are YouTube and QuickTime. */ - public List

* See https://developers.themoviedb.org/3/movies/get-upcoming */ - public MovieResultsPage getUpcoming(String language, Integer page, String region) { + public MovieResultsPage getUpcoming(String language, Integer page, String region) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, MovieMethod.upcoming); apiUrl.addLanguage(language); @@ -289,7 +290,7 @@ public MovieResultsPage getUpcoming(String language, Integer page, String region * * This is a curated list that will normally contain 100 movies. The default response will return 20 movies. */ - public MovieResultsPage getNowPlayingMovies(String language, Integer page, String region) { + public MovieResultsPage getNowPlayingMovies(String language, Integer page, String region) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, MovieMethod.now_playing); apiUrl.addLanguage(language); @@ -308,7 +309,7 @@ public MovieResultsPage getNowPlayingMovies(String language, Integer page, Strin * * This list is updated daily. The default response will return 20 movies. */ - public MovieResultsPage getPopularMovies(String language, Integer page) { + public MovieResultsPage getPopularMovies(String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, MovieMethod.popular); apiUrl.addLanguage(language); @@ -323,7 +324,7 @@ public MovieResultsPage getPopularMovies(String language, Integer page) { * * The default response will return 20 movies. */ - public MovieResultsPage getTopRatedMovies(String language, Integer page) { + public MovieResultsPage getTopRatedMovies(String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, MovieMethod.top_rated); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbPeople.java b/src/main/java/info/movito/themoviedbapi/TmdbPeople.java index 18177aef..0143108d 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbPeople.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbPeople.java @@ -4,6 +4,7 @@ import info.movito.themoviedbapi.model.ArtworkType; import info.movito.themoviedbapi.model.MovieImages; import info.movito.themoviedbapi.model.core.ResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.people.Person; import info.movito.themoviedbapi.model.people.PersonCredits; import info.movito.themoviedbapi.model.people.PersonPeople; @@ -31,7 +32,7 @@ public class TmdbPeople extends AbstractTmdbApi { * * It will return the single highest rated profile image. */ - public PersonPeople getPersonInfo(int personId, String... appendToResponse) { + public PersonPeople getPersonInfo(int personId, String... appendToResponse) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_PERSON, personId); apiUrl.appendToResponse(appendToResponse); @@ -44,7 +45,7 @@ public PersonPeople getPersonInfo(int personId, String... appendToResponse) { * * It will return the single highest rated poster for each movie record. */ - public PersonCredits getCombinedPersonCredits(int personId) { + public PersonCredits getCombinedPersonCredits(int personId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_PERSON, personId, "combined_credits"); return mapJsonResult(apiUrl, PersonCredits.class); @@ -53,7 +54,7 @@ public PersonCredits getCombinedPersonCredits(int personId) { /** * This method is used to retrieve all of the profile images for a person. */ - public List getPersonImages(int personId) { + public List getPersonImages(int personId) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_PERSON, personId, "images"); return mapJsonResult(apiUrl, MovieImages.class).getAll(ArtworkType.PROFILE); @@ -79,7 +80,7 @@ public void getPersonChanges(int personId, String startDate, String endDate) { * * This list refreshes every day. */ - public PersonResultsPage getPersonPopular(Integer page) { + public PersonResultsPage getPersonPopular(Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_PERSON, "popular"); apiUrl.addPage(page); @@ -90,7 +91,7 @@ public PersonResultsPage getPersonPopular(Integer page) { /** * Get the latest person id. */ - public PersonPeople getPersonLatest() { + public PersonPeople getPersonLatest() throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_PERSON, "latest"); return mapJsonResult(apiUrl, PersonPeople.class); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbReviews.java b/src/main/java/info/movito/themoviedbapi/TmdbReviews.java index dd1a5e40..1aafdb7d 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbReviews.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbReviews.java @@ -2,6 +2,7 @@ import info.movito.themoviedbapi.model.Reviews; import info.movito.themoviedbapi.model.core.ResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import static info.movito.themoviedbapi.TmdbMovies.TMDB_METHOD_MOVIE; @@ -26,7 +27,7 @@ public class TmdbReviews extends AbstractTmdbApi { * @param page the page * @return the reviews */ - public ReviewResultsPage getReviews(int movieId, String language, Integer page) { + public ReviewResultsPage getReviews(int movieId, String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_MOVIE, movieId, "reviews"); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbSearch.java b/src/main/java/info/movito/themoviedbapi/TmdbSearch.java index dd96b77e..a1dfc816 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbSearch.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbSearch.java @@ -5,6 +5,7 @@ import info.movito.themoviedbapi.model.Multi; import info.movito.themoviedbapi.model.core.MovieResultsPage; import info.movito.themoviedbapi.model.core.ResultsPage; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.keywords.Keyword; import info.movito.themoviedbapi.tools.ApiUrl; @@ -43,7 +44,7 @@ public TmdbSearch(TmdbApi tmdbApi) { * @param page The page of results to return. 0 to get the default (first page). */ public MovieResultsPage searchMovie(String query, Integer searchYear, String language, boolean includeAdult, - Integer page) { + Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, TMDB_METHOD_MOVIE); if (isBlank(query)) { @@ -77,7 +78,7 @@ public MovieResultsPage searchMovie(String query, Integer searchYear, String lan * @param page The page of results to return. 0 to get the default (first page). */ public TvResultsPage searchTv(String query, Integer searchYear, String language, boolean includeAdult, - Integer page) { + Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, TMDB_METHOD_TV); if (isBlank(query)) { @@ -109,7 +110,8 @@ public TvResultsPage searchTv(String query, Integer searchYear, String language, * @param includeAdult Whether to include adult titles in the search. * @param page The page of results to return. 0 to get the default (first page). */ - public CollectionResultsPage searchCollection(String query, String language, boolean includeAdult, Integer page) { + public CollectionResultsPage searchCollection(String query, String language, boolean includeAdult, Integer page) + throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, TMDB_METHOD_COLLECTION); if (isNotBlank(query)) { @@ -131,7 +133,7 @@ public CollectionResultsPage searchCollection(String query, String language, boo * The idea is to be a quick and light method so you can iterate through people quickly. */ - public TmdbPeople.PersonResultsPage searchPerson(String query, boolean includeAdult, Integer page) { + public TmdbPeople.PersonResultsPage searchPerson(String query, boolean includeAdult, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, TmdbPeople.TMDB_METHOD_PERSON); apiUrl.addPathParam(PARAM_QUERY, query); @@ -146,7 +148,7 @@ public TmdbPeople.PersonResultsPage searchPerson(String query, boolean includeAd /** * Search for lists by name and description. */ - public TmdbAccount.MovieListResultsPage searchList(String query, String language, Integer page) { + public TmdbAccount.MovieListResultsPage searchList(String query, String language, Integer page) throws TmdbResponseException { System.err.println("This method is part of the API but seems currently not available. " + "See https://www.themoviedb.org/talk/593409e3c3a36859ef01eddb#597124f8c3a3681608008424"); @@ -171,7 +173,7 @@ public TmdbAccount.MovieListResultsPage searchList(String query, String language * * http://help.themoviedb.org/kb/api/search-companies */ - public CompanyResultsPage searchCompany(String companyName, Integer page) { + public CompanyResultsPage searchCompany(String companyName, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, "company"); apiUrl.addPathParam(PARAM_QUERY, companyName); @@ -183,7 +185,7 @@ public CompanyResultsPage searchCompany(String companyName, Integer page) { /** * Search for keywords by name. */ - public KeywordResultsPage searchKeyword(String query, Integer page) { + public KeywordResultsPage searchKeyword(String query, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, "keyword"); if (isNotBlank(query)) { @@ -201,7 +203,7 @@ public KeywordResultsPage searchKeyword(String query, Integer page) { * * @return ResultsPage of Multi. */ - public MultiListResultsPage searchMulti(String query, String language, Integer page) { + public MultiListResultsPage searchMulti(String query, String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_SEARCH, TMDB_METHOD_MULTI); if (isBlank(query)) { diff --git a/src/main/java/info/movito/themoviedbapi/TmdbTV.java b/src/main/java/info/movito/themoviedbapi/TmdbTV.java index b5c29270..53f66e67 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbTV.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbTV.java @@ -5,6 +5,7 @@ import info.movito.themoviedbapi.model.MovieImages; import info.movito.themoviedbapi.model.config.Timezone; import info.movito.themoviedbapi.model.core.TvKeywords; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.tv.TvSeries; import info.movito.themoviedbapi.tools.ApiUrl; @@ -43,7 +44,7 @@ public class TmdbTV extends AbstractTmdbApi { /** * This method is used to retrieve all the basic series information. */ - public TvSeries getSeries(int seriesId, String language, TvMethod... appendToResponse) { + public TvSeries getSeries(int seriesId, String language, TvMethod... appendToResponse) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId); apiUrl.addLanguage(language); @@ -60,7 +61,7 @@ public TvSeries getSeries(int seriesId, String language, TvMethod... appendToRes * @param language the language * @return the show's credits */ - public Credits getCredits(int seriesId, String language) { + public Credits getCredits(int seriesId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TMDB_METHOD_CREDITS); apiUrl.addLanguage(language); @@ -74,7 +75,7 @@ public Credits getCredits(int seriesId, String language) { * @param page the page * @return the series */ - public TvResultsPage getPopular(String language, Integer page) { + public TvResultsPage getPopular(String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, TMDB_METHOD_POPULAR); apiUrl.addLanguage(language); @@ -91,7 +92,7 @@ public TvResultsPage getPopular(String language, Integer page) { * @param timezone the timezone * @return the series */ - public TvResultsPage getAiringToday(String language, Integer page, Timezone timezone) { + public TvResultsPage getAiringToday(String language, Integer page, Timezone timezone) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, TMDB_METHOD_AIRINGTODAY); apiUrl.addLanguage(language); @@ -111,7 +112,7 @@ public TvResultsPage getAiringToday(String language, Integer page, Timezone time * @param page the page * @return the series */ - public TvResultsPage getOnTheAir(String language, Integer page) { + public TvResultsPage getOnTheAir(String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, TMDB_METHOD_ONTHEAIR); apiUrl.addLanguage(language); @@ -127,7 +128,7 @@ public TvResultsPage getOnTheAir(String language, Integer page) { * @param page the page * @return the series */ - public TvResultsPage getTopRated(String language, Integer page) { + public TvResultsPage getTopRated(String language, Integer page) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, TMDB_METHOD_TOPRATED); apiUrl.addLanguage(language); @@ -143,7 +144,7 @@ public TvResultsPage getTopRated(String language, Integer page) { * @param language the language * @return the series */ - public MovieImages getImages(int seriesId, String language) { + public MovieImages getImages(int seriesId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TvMethod.images); apiUrl.addLanguage(language); @@ -158,7 +159,7 @@ public MovieImages getImages(int seriesId, String language) { * @param language the language * @return the keywords */ - public TvKeywords getKeywords(int seriesId, String language) { + public TvKeywords getKeywords(int seriesId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TMDB_METHOD_KEYWORDS); apiUrl.addLanguage(language); @@ -173,7 +174,7 @@ public TvKeywords getKeywords(int seriesId, String language) { * @param language the language * @return the content rating */ - public ContentRating.Results getContentRating(int seriesId, String language) { + public ContentRating.Results getContentRating(int seriesId, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TMDB_METHOD_CONTENT_RATING); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbTimezones.java b/src/main/java/info/movito/themoviedbapi/TmdbTimezones.java index abc6794b..e11444c4 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbTimezones.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbTimezones.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import info.movito.themoviedbapi.model.config.Timezone; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.tools.ApiUrl; import info.movito.themoviedbapi.tools.RequestType; @@ -33,14 +34,14 @@ public class TmdbTimezones extends AbstractTmdbApi { */ @Deprecated @SuppressWarnings("unchecked") - public List getTimezones() { + public List getTimezones() throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TmdbTimezones.TMDB_METHOD_TIMEZONESLIST); - String webpage = tmdbApi.requestWebPage(apiUrl, null, RequestType.GET); + String webpage = tmdbApi.getTmdbUrlReader().readUrl(apiUrl.buildUrl(), null, RequestType.GET); HashMap[] hashMaps1; try { - hashMaps1 = jsonMapper.readValue(webpage, HashMap[].class); + hashMaps1 = getObjectMapper().readValue(webpage, HashMap[].class); } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbTvEpisodes.java b/src/main/java/info/movito/themoviedbapi/TmdbTvEpisodes.java index 45155682..7fe2df1e 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbTvEpisodes.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbTvEpisodes.java @@ -1,6 +1,7 @@ package info.movito.themoviedbapi; import info.movito.themoviedbapi.model.Credits; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.tv.TvEpisode; import info.movito.themoviedbapi.tools.ApiUrl; @@ -27,7 +28,7 @@ public class TmdbTvEpisodes extends AbstractTmdbApi { * Gets the details for a tv episode. */ public TvEpisode getEpisode(int seriesId, int seasonNumber, int episodeNumber, String language, - EpisodeMethod... appendToResponse) { + EpisodeMethod... appendToResponse) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TMDB_METHOD_TV_SEASON, seasonNumber, TMDB_METHOD_TV_EPISODE, episodeNumber); @@ -41,7 +42,7 @@ public TvEpisode getEpisode(int seriesId, int seasonNumber, int episodeNumber, S /** * Gets the credits for a tv episode. */ - public Credits getCredits(int seriesId, int seasonNumber, int episodeNumber, String language) { + public Credits getCredits(int seriesId, int seasonNumber, int episodeNumber, String language) throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TMDB_METHOD_TV_SEASON, seasonNumber, TMDB_METHOD_TV_EPISODE, episodeNumber, credits); diff --git a/src/main/java/info/movito/themoviedbapi/TmdbTvSeasons.java b/src/main/java/info/movito/themoviedbapi/TmdbTvSeasons.java index fc78c5df..6eff8d2d 100644 --- a/src/main/java/info/movito/themoviedbapi/TmdbTvSeasons.java +++ b/src/main/java/info/movito/themoviedbapi/TmdbTvSeasons.java @@ -1,5 +1,6 @@ package info.movito.themoviedbapi; +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; import info.movito.themoviedbapi.model.tv.TvSeason; import info.movito.themoviedbapi.tools.ApiUrl; @@ -23,7 +24,8 @@ public class TmdbTvSeasons extends AbstractTmdbApi { /** * Gets the details for a tv season. */ - public TvSeason getSeason(int seriesId, int seasonNumber, String language, SeasonMethod... appendToResponse) { + public TvSeason getSeason(int seriesId, int seasonNumber, String language, SeasonMethod... appendToResponse) + throws TmdbResponseException { ApiUrl apiUrl = new ApiUrl(TMDB_METHOD_TV, seriesId, TMDB_METHOD_TV_SEASON, seasonNumber); apiUrl.addLanguage(language); diff --git a/src/main/java/info/movito/themoviedbapi/Utils.java b/src/main/java/info/movito/themoviedbapi/Utils.java index d2f5bdee..423228e9 100644 --- a/src/main/java/info/movito/themoviedbapi/Utils.java +++ b/src/main/java/info/movito/themoviedbapi/Utils.java @@ -120,10 +120,9 @@ public static URL createImageUrl(TmdbApi tmdb, String imagePath, String required /** * Use Jackson to convert Map to json string. */ - // fixme why is the argument not used?? - public static String convertToJson(ObjectMapper jsonMapper, Map map) { + public static String convertToJson(ObjectMapper objectMapper, Map map) { try { - return new ObjectMapper().writeValueAsString(map); + return objectMapper.writeValueAsString(map); } catch (JsonProcessingException jpe) { throw new RuntimeException("json conversion failed", jpe); diff --git a/src/main/java/info/movito/themoviedbapi/model/MovieListCreationStatus.java b/src/main/java/info/movito/themoviedbapi/model/MovieListCreationStatus.java index d7afd733..f75940eb 100644 --- a/src/main/java/info/movito/themoviedbapi/model/MovieListCreationStatus.java +++ b/src/main/java/info/movito/themoviedbapi/model/MovieListCreationStatus.java @@ -1,7 +1,7 @@ package info.movito.themoviedbapi.model; import com.fasterxml.jackson.annotation.JsonProperty; -import info.movito.themoviedbapi.model.core.ResponseStatus; +import info.movito.themoviedbapi.model.core.responses.ResponseStatus; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/src/main/java/info/movito/themoviedbapi/model/core/ResponseStatusException.java b/src/main/java/info/movito/themoviedbapi/model/core/ResponseStatusException.java deleted file mode 100644 index 72efb370..00000000 --- a/src/main/java/info/movito/themoviedbapi/model/core/ResponseStatusException.java +++ /dev/null @@ -1,23 +0,0 @@ -package info.movito.themoviedbapi.model.core; - -import info.movito.themoviedbapi.tools.MovieDbException; -import lombok.Getter; - -@Getter -public class ResponseStatusException extends MovieDbException { - private final ResponseStatus responseStatus; - - /** - * Constructor. - */ - public ResponseStatusException(ResponseStatus responseStatus) { - super(responseStatus.getStatusCode() + " :: " + responseStatus.getStatusMessage()); - - this.responseStatus = responseStatus; - } - - @Override - public String toString() { - return responseStatus.toString(); - } -} diff --git a/src/main/java/info/movito/themoviedbapi/model/core/ResponseStatus.java b/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatus.java similarity index 60% rename from src/main/java/info/movito/themoviedbapi/model/core/ResponseStatus.java rename to src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatus.java index 688112fa..b6bcf0d8 100644 --- a/src/main/java/info/movito/themoviedbapi/model/core/ResponseStatus.java +++ b/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatus.java @@ -1,16 +1,10 @@ -package info.movito.themoviedbapi.model.core; +package info.movito.themoviedbapi.model.core.responses; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; +import info.movito.themoviedbapi.model.core.AbstractJsonMapping; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -/** - * See https://www.themoviedb.org/documentation/api/status-codes. - */ -@AllArgsConstructor -@NoArgsConstructor @Data @EqualsAndHashCode(callSuper = false) public class ResponseStatus extends AbstractJsonMapping { diff --git a/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusAuthentication.java b/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusAuthentication.java new file mode 100644 index 00000000..ad6ed650 --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusAuthentication.java @@ -0,0 +1,12 @@ +package info.movito.themoviedbapi.model.core.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ResponseStatusAuthentication extends ResponseStatus { + @JsonProperty("success") + private Boolean success; +} diff --git a/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusDelete.java b/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusDelete.java new file mode 100644 index 00000000..55f72c51 --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/model/core/responses/ResponseStatusDelete.java @@ -0,0 +1,13 @@ +package info.movito.themoviedbapi.model.core.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import info.movito.themoviedbapi.model.core.AbstractJsonMapping; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ResponseStatusDelete extends AbstractJsonMapping { + @JsonProperty("success") + private Boolean success; +} diff --git a/src/main/java/info/movito/themoviedbapi/model/core/responses/TmdbResponseException.java b/src/main/java/info/movito/themoviedbapi/model/core/responses/TmdbResponseException.java new file mode 100644 index 00000000..4a15a9cb --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/model/core/responses/TmdbResponseException.java @@ -0,0 +1,27 @@ +package info.movito.themoviedbapi.model.core.responses; + +import info.movito.themoviedbapi.tools.TmdbException; +import info.movito.themoviedbapi.tools.TmdbResponseCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class TmdbResponseException extends TmdbException { + private final TmdbResponseCode responseCode; + + public TmdbResponseException(TmdbResponseCode responseCode) { + super(responseCode.toString()); + this.responseCode = responseCode; + } + + public TmdbResponseException(String message) { + super(message); + this.responseCode = null; + } + + public TmdbResponseException(Exception exception) { + super(exception); + this.responseCode = null; + } +} diff --git a/src/main/java/info/movito/themoviedbapi/tools/RequestType.java b/src/main/java/info/movito/themoviedbapi/tools/RequestType.java index 3340bc93..d4455669 100644 --- a/src/main/java/info/movito/themoviedbapi/tools/RequestType.java +++ b/src/main/java/info/movito/themoviedbapi/tools/RequestType.java @@ -5,5 +5,6 @@ */ public enum RequestType { GET, + POST, DELETE } diff --git a/src/main/java/info/movito/themoviedbapi/tools/TmdbException.java b/src/main/java/info/movito/themoviedbapi/tools/TmdbException.java new file mode 100644 index 00000000..e48e6405 --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/tools/TmdbException.java @@ -0,0 +1,18 @@ +package info.movito.themoviedbapi.tools; + +/** + * TMDb-API related exceptions. + */ +public class TmdbException extends Exception { + public TmdbException(String message) { + super(message); + } + + public TmdbException(Exception exception) { + super(exception); + } + + public TmdbException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/info/movito/themoviedbapi/tools/TmdbHttpClient.java b/src/main/java/info/movito/themoviedbapi/tools/TmdbHttpClient.java new file mode 100644 index 00000000..16d4b14c --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/tools/TmdbHttpClient.java @@ -0,0 +1,49 @@ +package info.movito.themoviedbapi.tools; + +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; +import lombok.AllArgsConstructor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +import java.io.IOException; +import java.net.URL; + +/** + * Class to make requests to the movie database api. + */ +@AllArgsConstructor +public class TmdbHttpClient implements TmdbUrlReader { + private static final OkHttpClient okHttpClient = new OkHttpClient(); + + private final String apiKey; + + @Override + public String readUrl(URL url, String jsonBody, RequestType requestType) throws TmdbResponseException { + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .addHeader("Authorization", "Bearer " + apiKey) + .addHeader("Accept", "application/json") + .addHeader("Content-type", "application/json"); + + switch (requestType) { + case GET -> requestBuilder.get(); + case POST -> requestBuilder.post(okhttp3.RequestBody.create(jsonBody, okhttp3.MediaType.parse("application/json"))); + case DELETE -> requestBuilder.delete(); + default -> throw new RuntimeException("Invalid request type: " + requestType); + } + + try (Response response = okHttpClient.newCall(requestBuilder.build()).execute()) { + ResponseBody responseBody = response.body(); + if (responseBody == null) { + throw new TmdbResponseException("Response body was null: " + response); + } + + return responseBody.string(); + } + catch (IOException exception) { + throw new TmdbResponseException(exception); + } + } +} diff --git a/src/main/java/info/movito/themoviedbapi/tools/TmdbResponseCode.java b/src/main/java/info/movito/themoviedbapi/tools/TmdbResponseCode.java new file mode 100644 index 00000000..02bbc5ab --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/tools/TmdbResponseCode.java @@ -0,0 +1,121 @@ +package info.movito.themoviedbapi.tools; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.util.Arrays; +import java.util.Set; + +/** + * TMDb-API related response codes. See the + * documentation for more info. + */ +@Getter +@RequiredArgsConstructor +@ToString +public enum TmdbResponseCode { + SUCCESS(1, 200, true, "Success."), + INVALID_SERVICE(2, 501, false, "Invalid service: this service does not exist."), + AUTHENTICATION_FAILED(3, 401, false, "Authentication failed: You do not have permissions to access the service."), + INVALID_FORMAT(4, 405, false, "Invalid format: This service doesn't exist in that format."), + INVALID_PARAMETERS(5, 422, false, "Invalid parameters: Your request parameters are incorrect."), + INVALID_PRE_REQUISITE_ID(6, 404, false, "Invalid id: The pre-requisite id is invalid or not found."), + INVALID_API_KEY(7, 401, false, "Invalid API key: You must be granted a valid key."), + DUPLICATE_ENTRY(8, 403, false, "Duplicate entry: The data you tried to submit already exists."), + SERVICE_OFFLINE(9, 503, false, "Service offline: This service is temporarily offline, try again later."), + SUSPENDED_API_KEY(10, 401, false, "Suspended API key: Access to your account has been suspended, contact TMDB."), + INTERNAL_ERROR(11, 500, false, "Internal error: Something went wrong, contact TMDB."), + ITEM_UPDATED(12, 201, true, "The item/record was updated successfully."), + ITEM_DELETED(13, 200, true, "The item/record was deleted successfully."), + AUTHENTICATION_FAILED_2(14, 401, false, "Authentication failed."), + FAILED(15, 500, false, "Failed."), + DEVICE_DENIED(16, 401, false, "Device denied."), + SESSION_DENIED(17, 401, false, "Session denied."), + VALIDATION_FAILED(18, 400, false, "Validation failed."), + INVALID_ACCEPT_HEADER(19, 406, false, "Invalid accept header."), + INVALID_DATE_RANGE(20, 422, false, "Invalid date range: Should be a range no longer than 14 days."), + ENTRY_NOT_FOUND(21, 200, false, "Entry not found: The item you are trying to edit cannot be found."), + INVALID_PAGE(22, 400, false, "Invalid page: Pages start at 1 and max at 500. They are expected to be an integer."), + INVALID_DATE(23, 400, false, "Invalid date: Format needs to be YYYY-MM-DD."), + REQUEST_TIMEOUT(24, 504, false, "Your request to the backend server timed out. Try again."), + REQUEST_LIMIT_EXCEEDED(25, 429, false, "Your request count (#) is over the allowed limit of (40)."), + USERNAME_AND_PASSWORD_REQUIRED(26, 400, false, "You must provide a username and password."), + TOO_MANY_APPEND_TO_RESPONSE_OBJECTS(27, 400, false, "Too many append to response objects: The maximum number of remote calls is 20."), + INVALID_TIMEZONE(28, 400, false, "Invalid timezone: Please consult the documentation for a valid timezone."), + ACTION_CONFIRMATION_REQUIRED(29, 400, false, "You must confirm this action: Please provide a confirm=true parameter."), + INVALID_USERNAME_AND_OR_PASSWORD(30, 401, false, "Invalid username and/or password: You did not provide a valid login."), + ACCOUNT_DISABLED(31, 401, false, "Account disabled: Your account is no longer active. Contact TMDB if this is an error."), + EMAIL_NOT_VERIFIED(32, 401, false, "Email not verified: Your email address has not been verified."), + INVALID_REQUEST_TOKEN(33, 401, false, "Invalid request token: The request token is either expired or invalid."), + RESOURCE_NOT_FOUND(34, 404, false, "The resource you requested could not be found."), + INVALID_TOKEN(35, 401, false, "Invalid token."), + TOKEN_NOT_GRANTED_WRITE_PERMISSION(36, 401, false, "This token hasn't been granted write permission by the user."), + REQUESTED_SESSION_NOT_FOUND(37, 404, false, "The requested session could not be found."), + PERMISSION_DENIED(38, 401, false, "You don't have permission to edit this resource."), + RESOURCE_PRIVATE(39, 401, false, "This resource is private."), + NOTHING_TO_UPDATE(40, 200, false, "Nothing to update."), + REQUEST_TOKEN_NOT_APPROVED(41, 422, false, "This request token hasn't been approved by the user."), + REQUEST_METHOD_NOT_SUPPORTED(42, 405, false, "This request method is not supported for this resource."), + COULD_NOT_CONNECT_TO_BACKEND_SERVER(43, 502, false, "Couldn't connect to the backend server."), + INVALID_ID(44, 500, false, "The ID is invalid."), + USER_SUSPENDED(45, 403, false, "This user has been suspended."), + API_UNDERGOING_MAINTENANCE(46, 503, false, "The API is undergoing maintenance. Try again later."), + INVALID_INPUT(47, 400, false, "The input is not valid."); + + private final int tmdbCode; + + private final int httpStatus; + + private final boolean success; + + private final String message; + + /** + * Returns the {@link TmdbResponseCode} for the given tmdbCode. + * + * @param tmdbCode the 'status_code'. + */ + public static TmdbResponseCode fromCode(int tmdbCode) { + return Arrays.stream(TmdbResponseCode.values()) + .filter(responseCode -> responseCode.getTmdbCode() == tmdbCode) + .findFirst() + .orElse(null); + } + + /** + * Returns all the error responses. + */ + public static Set getErrorResponses() { + return Arrays.stream(TmdbResponseCode.values()) + .filter(responseCode -> !responseCode.isSuccess()) + .collect(java.util.stream.Collectors.toSet()); + } + + /** + * Returns all the successful responses. + */ + public static Set getSuccessResponses() { + return Arrays.stream(TmdbResponseCode.values()) + .filter(TmdbResponseCode::isSuccess) + .collect(java.util.stream.Collectors.toSet()); + } + + /** + * Returns all the error codes. + */ + public static Set getErrorCodes() { + return getErrorResponses().stream() + .map(TmdbResponseCode::getTmdbCode) + .collect(java.util.stream.Collectors.toSet()); + } + + /** + * Returns all the successful codes. + */ + public static Set getSuccessCodes() { + return getSuccessResponses().stream() + .map(TmdbResponseCode::getTmdbCode) + .collect(java.util.stream.Collectors.toSet()); + } +} diff --git a/src/main/java/info/movito/themoviedbapi/tools/TmdbUrlReader.java b/src/main/java/info/movito/themoviedbapi/tools/TmdbUrlReader.java new file mode 100644 index 00000000..52ffc6af --- /dev/null +++ b/src/main/java/info/movito/themoviedbapi/tools/TmdbUrlReader.java @@ -0,0 +1,21 @@ +package info.movito.themoviedbapi.tools; + +import info.movito.themoviedbapi.model.core.responses.TmdbResponseException; + +import java.net.URL; + +/** + * Interface for reading URLs from TMDB API. + */ +public interface TmdbUrlReader { + /** + * Reads a url and returns the response. + * + * @param url the url to make the request to + * @param jsonBody the json body to send with the request + * @param requestType the type of request to make + * @return the response from the movie database api + * @throws TmdbResponseException if the response was not successful + */ + String readUrl(URL url, String jsonBody, RequestType requestType) throws TmdbResponseException; +} diff --git a/src/main/java/info/movito/themoviedbapi/tools/UrlReader.java b/src/main/java/info/movito/themoviedbapi/tools/UrlReader.java deleted file mode 100644 index e7f81ec1..00000000 --- a/src/main/java/info/movito/themoviedbapi/tools/UrlReader.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.movito.themoviedbapi.tools; - -import java.net.URL; - -/** - * Interface for reading URLs. - */ -public interface UrlReader { - /** - * Send a request to the specified URL. - */ - String request(URL url, String jsonBody, RequestType requestType); -} diff --git a/src/main/java/info/movito/themoviedbapi/tools/WebBrowser.java b/src/main/java/info/movito/themoviedbapi/tools/WebBrowser.java deleted file mode 100644 index 52431676..00000000 --- a/src/main/java/info/movito/themoviedbapi/tools/WebBrowser.java +++ /dev/null @@ -1,349 +0,0 @@ -package info.movito.themoviedbapi.tools; - -import org.apache.commons.codec.binary.Base64; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Web browser with simple cookies support. - */ -public final class WebBrowser implements UrlReader { - private static final Logger logger = LoggerFactory.getLogger(WebBrowser.class); - - private static final Map browserProperties = new HashMap<>(); - - private static final Map> cookies = new HashMap<>(); - - private static String proxyHost = null; - - private static String proxyPort = null; - - private static String proxyUsername = null; - - private static String proxyPassword = null; - - private static String proxyEncodedPassword = null; - - private static int webTimeoutConnect = 25000; // 25 second timeout - - private static int webTimeoutRead = 90000; // 90 second timeout - - /** - * Populate the browser properties. - */ - private static void populateBrowserProperties() { - if (browserProperties.isEmpty()) { - browserProperties.put("User-Agent", "Mozilla/5.25 Netscape/5.0 (Windows; I; Win95)"); - browserProperties.put("Accept", "application/json"); - browserProperties.put("Content-type", "application/json"); - } - } - - /** - * Opens a proxied connection. - */ - public static URLConnection openProxiedConnection(URL url) { - try { - if (proxyHost != null) { - System.getProperties().put("proxySet", "true"); - System.getProperties().put("proxyHost", proxyHost); - System.getProperties().put("proxyPort", proxyPort); - } - - URLConnection cnx = url.openConnection(); - - if (proxyUsername != null) { - cnx.setRequestProperty("Proxy-Authorization", proxyEncodedPassword); - } - - return cnx; - } - catch (IOException ex) { - throw new MovieDbException(url.toString(), ex); - } - } - - private static void sendHeader(URLConnection cnx) { - populateBrowserProperties(); - - // send browser properties - for (Map.Entry browserProperty : browserProperties.entrySet()) { - cnx.setRequestProperty(browserProperty.getKey(), browserProperty.getValue()); - } - // send cookies - String cookieHeader = createCookieHeader(cnx); - if (!cookieHeader.isEmpty()) { - cnx.setRequestProperty("Cookie", cookieHeader); - } - } - - private static String createCookieHeader(URLConnection cnx) { - String host = cnx.getURL().getHost(); - StringBuilder cookiesHeader = new StringBuilder(); - for (Map.Entry> domainCookies : cookies.entrySet()) { - if (host.endsWith(domainCookies.getKey())) { - for (Map.Entry cookie : domainCookies.getValue().entrySet()) { - cookiesHeader.append(cookie.getKey()); - cookiesHeader.append("="); - cookiesHeader.append(cookie.getValue()); - cookiesHeader.append(";"); - } - } - } - if (!cookiesHeader.isEmpty()) { - // remove last ; char - cookiesHeader.deleteCharAt(cookiesHeader.length() - 1); - } - return cookiesHeader.toString(); - } - - private static void readHeader(URLConnection cnx) { - // read new cookies and update our cookies - for (Map.Entry> header : cnx.getHeaderFields().entrySet()) { - if ("Set-Cookie".equals(header.getKey())) { - for (String cookieHeader : header.getValue()) { - String[] cookieElements = cookieHeader.split(" *; *"); - if (cookieElements.length >= 1) { - String[] firstElem = cookieElements[0].split(" *= *"); - String cookieName = firstElem[0]; - String cookieValue = firstElem.length > 1 ? firstElem[1] : null; - String cookieDomain = null; - // find cookie domain - for (int i = 1; i < cookieElements.length; i++) { - String[] cookieElement = cookieElements[i].split(" *= *"); - if ("domain".equals(cookieElement[0])) { - cookieDomain = cookieElement.length > 1 ? cookieElement[1] : null; - break; - } - } - if (cookieDomain == null) { - // if domain isn't set take current host - cookieDomain = cnx.getURL().getHost(); - } - Map domainCookies = cookies.computeIfAbsent(cookieDomain, k -> new HashMap<>()); - // add or replace cookie - domainCookies.put(cookieName, cookieValue); - } - } - } - } - } - - private static Charset getCharset(URLConnection cnx) { - Charset charset = null; - // content type will be string like "text/html; charset=UTF-8" or "text/html" - String contentType = cnx.getContentType(); - if (contentType != null) { - // changed 'charset' to 'harset' in regexp because some sites send 'Charset' - Matcher m = Pattern.compile("harset *=[ '\"]*([^ ;'\"]+)[ ;'\"]*").matcher(contentType); - if (m.find()) { - String encoding = m.group(1); - try { - charset = Charset.forName(encoding); - } - catch (UnsupportedCharsetException e) { - // there will be used default charset - } - } - } - if (charset == null) { - charset = Charset.defaultCharset(); - } - - return charset; - } - - public static String getProxyHost() { - return proxyHost; - } - - public static void setProxyHost(String myProxyHost) { - WebBrowser.proxyHost = myProxyHost; - } - - public static String getProxyPort() { - return proxyPort; - } - - public static void setProxyPort(String myProxyPort) { - WebBrowser.proxyPort = myProxyPort; - } - - public static String getProxyUsername() { - return proxyUsername; - } - - public static void setProxyUsername(String myProxyUsername) { - WebBrowser.proxyUsername = myProxyUsername; - } - - public static String getProxyPassword() { - return proxyPassword; - } - - /** - * Sets the proxy password and encodes it. - */ - public static void setProxyPassword(String myProxyPassword) { - WebBrowser.proxyPassword = myProxyPassword; - - if (proxyUsername != null) { - proxyEncodedPassword = proxyUsername + ":" + proxyPassword; - proxyEncodedPassword = - "Basic " + new String(Base64.encodeBase64((proxyUsername + ":" + proxyPassword).getBytes())); - } - } - - public static int getWebTimeoutConnect() { - return webTimeoutConnect; - } - - public static void setWebTimeoutConnect(int webTimeoutConnect) { - WebBrowser.webTimeoutConnect = webTimeoutConnect; - } - - public static int getWebTimeoutRead() { - return webTimeoutRead; - } - - public static void setWebTimeoutRead(int webTimeoutRead) { - WebBrowser.webTimeoutRead = webTimeoutRead; - } - - @Override - public String request(URL url, String jsonBody, RequestType requestType) { - return request(url, jsonBody, requestType.equals(RequestType.DELETE)); - } - - /** - * Send a request to the specified URL. - */ - public static String request(URL url, String jsonBody, boolean isDeleteRequest) { - StringWriter content = null; - - try { - content = new StringWriter(); - - BufferedReader in = null; - HttpURLConnection cnx = null; - try { - cnx = (HttpURLConnection) openProxiedConnection(url); - - if (isDeleteRequest) { - cnx.setDoOutput(true); - cnx.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - cnx.setRequestMethod("DELETE"); - } - - sendHeader(cnx); - - if (jsonBody != null) { - cnx.setDoOutput(true); - OutputStreamWriter wr = new OutputStreamWriter(cnx.getOutputStream()); - wr.write(jsonBody); - wr.flush(); - } - - readHeader(cnx); - - // http://stackoverflow.com/questions/4633048/httpurlconnection-reading-response-content-on-403-error - if (cnx.getResponseCode() >= 400) { - // for some strange reason the error stream is sometimes null - // see http://stackoverflow.com/questions/6070350/errorstream-in-httpurlconnection - if (cnx.getErrorStream() == null) { - throw new MovieDbException("error stream was null"); - } - - in = new BufferedReader(new InputStreamReader(cnx.getErrorStream(), getCharset(cnx))); - } - else { - in = new BufferedReader(new InputStreamReader(cnx.getInputStream(), getCharset(cnx))); - } - - String line; - while ((line = in.readLine()) != null) { - content.write(line); - } - } - finally { - if (in != null) { - in.close(); - } - - if (cnx != null) { - cnx.disconnect(); - } - } - - // if we hit the request limit, throw special exception that indicates the required waiting period - if (cnx.getHeaderField("Retry-After") != null) { - throw new RequestCountLimitException(content.toString(), - Integer.parseInt(cnx.getHeaderField("Retry-After"))); - } - - return content.toString(); - } - catch (IOException ex) { - throw new MovieDbException(url.toString(), ex); - } - finally { - if (content != null) { - try { - content.close(); - } - catch (IOException ex) { - logger.debug("Failed to close connection: " + ex.getMessage()); - } - } - } - } - - /** - * Send a request to the specified URL. - */ - public String request(String url) { - try { - return request(new URL(url), null, RequestType.GET); - } - catch (MalformedURLException ex) { - throw new MovieDbException(url, ex); - } - } - - /** - * Set the proxy information. - */ - public void setProxy(String host, String port, String username, String password) { - - WebBrowser.setProxyHost(host); - WebBrowser.setProxyPort(port); - WebBrowser.setProxyUsername(username); - WebBrowser.setProxyPassword(password); - } - - /** - * Set the connection and read time out values. - */ - public void setTimeout(int connect, int read) { - - WebBrowser.setWebTimeoutConnect(connect); - WebBrowser.setWebTimeoutRead(read); - } -} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3d9ac327..a5c5095e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -7,12 +7,14 @@ requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.core; requires com.fasterxml.jackson.databind; + requires okhttp3; opens info.movito.themoviedbapi to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model.changes to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model.config to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model.core to com.fasterxml.jackson.databind; + opens info.movito.themoviedbapi.model.core.responses to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model.keywords to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model.people to com.fasterxml.jackson.databind; opens info.movito.themoviedbapi.model.providers to com.fasterxml.jackson.databind; @@ -24,6 +26,7 @@ exports info.movito.themoviedbapi.model.changes; exports info.movito.themoviedbapi.model.config; exports info.movito.themoviedbapi.model.core; + exports info.movito.themoviedbapi.model.core.responses; exports info.movito.themoviedbapi.model.keywords; exports info.movito.themoviedbapi.model.people; exports info.movito.themoviedbapi.model.providers;