SearchApi.java
/*
* tvmaze-java-client - A client to access the TVMaze API
* Copyright © 2024-2025 Andy Miles (andy.miles@amilesend.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.amilesend.tvmaze.client.api;
import com.amilesend.client.connection.Connection;
import com.amilesend.client.parse.parser.BasicParser;
import com.amilesend.client.parse.parser.ListParser;
import com.amilesend.tvmaze.client.model.Person;
import com.amilesend.tvmaze.client.model.Show;
import com.amilesend.tvmaze.client.model.type.PersonResult;
import com.amilesend.tvmaze.client.model.type.ShowResult;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.commons.lang3.Validate;
import java.util.List;
/**
* TVMaze API to search for show information.
* <br/>
* For more information, please refer to: <a href="https://www.tvmaze.com/api#search">https://www.tvmaze.com/api#search
* </a>
*/
@Slf4j
public class SearchApi extends ApiBase {
private static final String SEARCH_SHOWS_API_PATH = "/search/shows";
private static final String SINGLE_SEARCH_SHOWS_API_PATH = "/singlesearch/shows";
private static final String LOOKUP_SHOWS_API_PATH = "/lookup/shows";
private static final String SEARCH_PEOPLE_API_PATH = "/search/people";
private static final int MAX_QUERY_LENGTH = 256;
/**
* Creates a new {@code SearchApi} object.
*
* @param connection the connection
*/
public SearchApi(final Connection connection) {
super(connection);
}
////////////////
// searchShows
////////////////
/**
* Search for TV shows for the given query.
*
* @param query the search query
* @return the list of shows for the associated query
* @see ShowResult
*/
public List<ShowResult> searchShows(final String query) {
final HttpUrl url = validateAndFormatSearchUrl(SEARCH_SHOWS_API_PATH, query);
return connection.execute(
connection.newRequestBuilder()
.url(url)
.build(),
new ListParser<>(ShowResult.class));
}
/////////////////////
// singleSearchShow
/////////////////////
/**
* Search for and return a single show for the given query.
*
* @param query the search query
* @param includeEmbeddedTypes the optional embedded types to include in the show
* @return the tv show
* @see Show
*/
public Show singleSearchShow(final String query, final Show.EmbeddedType... includeEmbeddedTypes) {
final HttpUrl url = validateAndFormatSearchUrl(SINGLE_SEARCH_SHOWS_API_PATH, query, includeEmbeddedTypes);
return connection.execute(
connection.newRequestBuilder()
.url(url)
.build(),
new BasicParser<>(Show.class));
}
///////////////
// lookupShow
///////////////
/**
* Lookup a show based on an alternative external identifier.
*
* @param type the id type identifier
* @param externalId the external identifier
* @return the tv show
* @see ShowLookupIdType
* @see Show
*/
public Show lookupShow(final ShowLookupIdType type, final String externalId) {
final HttpUrl url = validateAndFormatLookupShowUrl(type, externalId);
return connection.execute(
connection.newRequestBuilder()
.url(url)
.build(),
new BasicParser<>(Show.class));
}
private HttpUrl validateAndFormatLookupShowUrl(@NonNull final ShowLookupIdType type, final String externalId) {
final String formattedId = validateId(externalId);
return HttpUrl.parse(
new StringBuilder(connection.getBaseUrl())
.append(LOOKUP_SHOWS_API_PATH)
.toString())
.newBuilder()
.addQueryParameter(type.getQueryParameter(), formattedId)
.build();
}
/////////////////
// searchPeople
/////////////////
/**
* Search for people (e.g., actors, directors, etc.).
*
* @param query the search query
* @return the list of persons associated with the query
* @see Person
* @see PersonResult
*/
public List<PersonResult> searchPeople(final String query) {
final HttpUrl url = validateAndFormatSearchUrl(SEARCH_PEOPLE_API_PATH, query);
return connection.execute(
connection.newRequestBuilder()
.url(url)
.build(),
new ListParser<>(PersonResult.class));
}
private HttpUrl validateAndFormatSearchUrl(
final String apiPath,
final String query,
final Show.EmbeddedType... includeEmbeddedTypes) {
Validate.notBlank(apiPath, "apiPath must not be blank");
final String formattedQuery = validateQuery(query);
final HttpUrl.Builder urlBuilder = HttpUrl.parse(
new StringBuilder(connection.getBaseUrl())
.append(apiPath)
.toString())
.newBuilder()
.addQueryParameter("q", formattedQuery);
final HttpUrl url = formatEmbeddedTypes(urlBuilder, includeEmbeddedTypes).build();
if (log.isDebugEnabled()) {
log.debug("Search URL: {}", url);
}
return url;
}
protected static String validateQuery(final String query) {
Validate.notBlank(query, "query must not be blank");
Validate.isTrue(query.length() <= MAX_QUERY_LENGTH,
"query length must be <= " + MAX_QUERY_LENGTH);
return query;
}
/** The type of alternative identifier type. */
@RequiredArgsConstructor
public enum ShowLookupIdType {
IMDB("imdb"),
TV_RAGE("tvrage"),
TVDB("thetvdb");
@Getter
private final String queryParameter;
}
}