Java tutorial
/* * Copyright (c) 2004-2016 YAMJ Members * https://github.com/orgs/YAMJ/people * * This file is part of the Yet Another Movie Jukebox (YAMJ) project. * * YAMJ 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 * any later version. * * YAMJ 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 YAMJ. If not, see <http://www.gnu.org/licenses/>. * * Web: https://github.com/YAMJ/yamj-v2 * */ package com.moviejukebox.plugin; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.pojava.datetime.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.moviejukebox.model.Filmography; import com.moviejukebox.model.Movie; import com.moviejukebox.model.Person; import com.moviejukebox.model.comparator.FilmographyDateComparator; import com.moviejukebox.scanner.MovieFilenameScanner; import com.moviejukebox.scanner.artwork.FanartScanner; import com.moviejukebox.tools.OverrideTools; import com.moviejukebox.tools.PropertiesUtil; import com.moviejukebox.tools.StringTools; import com.moviejukebox.tools.YamjHttpClientBuilder; import com.moviejukebox.tools.cache.CacheMemory; import com.omertron.themoviedbapi.Compare; import com.omertron.themoviedbapi.MovieDbException; import com.omertron.themoviedbapi.TheMovieDbApi; import com.omertron.themoviedbapi.enumeration.ArtworkType; import com.omertron.themoviedbapi.enumeration.SearchType; import com.omertron.themoviedbapi.model.Genre; import com.omertron.themoviedbapi.model.Language; import com.omertron.themoviedbapi.model.artwork.Artwork; import com.omertron.themoviedbapi.model.collection.Collection; import com.omertron.themoviedbapi.model.collection.CollectionInfo; import com.omertron.themoviedbapi.model.credits.CreditMovieBasic; import com.omertron.themoviedbapi.model.credits.MediaCreditCast; import com.omertron.themoviedbapi.model.credits.MediaCreditCrew; import com.omertron.themoviedbapi.model.media.MediaCreditList; import com.omertron.themoviedbapi.model.movie.MovieInfo; import com.omertron.themoviedbapi.model.movie.ProductionCompany; import com.omertron.themoviedbapi.model.movie.ProductionCountry; import com.omertron.themoviedbapi.model.movie.ReleaseInfo; import com.omertron.themoviedbapi.model.person.PersonCreditList; import com.omertron.themoviedbapi.model.person.PersonFind; import com.omertron.themoviedbapi.model.person.PersonInfo; import com.omertron.themoviedbapi.results.ResultList; /** * @author Stuart.Boston * @version 2.0 (18th October 2010) */ public class TheMovieDbPlugin implements MovieDatabasePlugin { private static final Logger LOG = LoggerFactory.getLogger(TheMovieDbPlugin.class); public static final String TMDB_PLUGIN_ID = "themoviedb"; public static final String IMDB_PLUGIN_ID = "imdb"; private TheMovieDbApi tmdb = null; private String languageCode; private String countryCode; private final boolean downloadFanart = PropertiesUtil.getBooleanProperty("fanart.movie.download", Boolean.FALSE); private static final String FANART_TOKEN = PropertiesUtil.getProperty("mjb.scanner.fanartToken", ".fanart"); private final String fanartExtension = PropertiesUtil.getProperty("fanart.format", "jpg"); public static final boolean INCLUDE_ADULT = PropertiesUtil.getBooleanProperty("themoviedb.includeAdult", Boolean.FALSE); public static final int SEARCH_MATCH = PropertiesUtil.getIntProperty("themoviedb.searchMatch", 5); private static final String LANGUAGE_DELIMITER = PropertiesUtil.getProperty("mjb.language.delimiter", Movie.SPACE_SLASH_SPACE); private static final boolean AUTO_COLLECTION = PropertiesUtil.getBooleanProperty("themoviedb.collection", Boolean.FALSE); public static final String CACHE_COLLECTION = "Collection"; public static final String CACHE_COLLECTION_IMAGES = "CollectionImages"; // People properties private final int preferredBiographyLength = PropertiesUtil.getIntProperty("plugin.biography.maxlength", 500); private final int preferredFilmographyMax = PropertiesUtil.getIntProperty("plugin.filmography.max", 20); private final boolean sortFilmographyAsc = PropertiesUtil.getBooleanProperty("plugin.filmography.sort.asc", Boolean.FALSE); private final FilmographyDateComparator filmographyCmp = new FilmographyDateComparator(sortFilmographyAsc); private final boolean skipFaceless = PropertiesUtil.getBooleanProperty("plugin.people.skip.faceless", Boolean.FALSE); private final int actorMax = PropertiesUtil.getReplacedIntProperty("movie.actor.maxCount", "plugin.people.maxCount.actor", 10); private final int directorMax = PropertiesUtil.getReplacedIntProperty("movie.director.maxCount", "plugin.people.maxCount.director", 2); private final int writerMax = PropertiesUtil.getReplacedIntProperty("movie.writer.maxCount", "plugin.people.maxCount.writer", 3); private final List<String> jobsInclude = Arrays.asList(PropertiesUtil .getProperty("plugin.filmography.jobsInclude", "Director,Writer,Actor,Actress").split(",")); // Literals private static final String LOG_LOCATE_MOVIE_INFORMATION = "{}: Using '{}' & '{}' to locate movie information"; private static final String TOKEN_SPLIT = "/ \n,:!&\"'(--_)=$"; private static final String ORIGINAL = "original"; public TheMovieDbPlugin() { try { String apiKey = PropertiesUtil.getProperty("API_KEY_TheMovieDB"); tmdb = new TheMovieDbApi(apiKey, YamjHttpClientBuilder.getHttpClient()); } catch (MovieDbException ex) { LOG.warn("Failed to initialise TheMovieDB API: {}", ex.getMessage()); return; } decodeLanguage(); } /** * Decode the language code property into language and country */ private void decodeLanguage() { String language = PropertiesUtil.getProperty("themoviedb.language", "en"); String country = PropertiesUtil.getProperty("themoviedb.country", ""); // Don't default this as we might get it from the language (old setting) if (language.length() > 2) { if (StringUtils.isBlank(country)) { // Guess that the last 2 characters of the language code is the country code. country = language.substring(language.length() - 2).toUpperCase(); } language = language.substring(0, 2).toLowerCase(); } // Default the country to US if (StringUtils.isBlank(country)) { country = "US"; } setLanguageCode(language); setCountryCode(country); LOG.debug("Using '{}' as the language code", language); LOG.debug("Using '{}' as the country code", country); } public void setLanguageCode(String languageCode) { this.languageCode = languageCode; } public void setCountryCode(String countryCode) { this.countryCode = countryCode; } @Override public String getPluginID() { return TMDB_PLUGIN_ID; } @Override public boolean scan(Movie movie) { String imdbID = movie.getId(IMDB_PLUGIN_ID); String tmdbID = movie.getId(TMDB_PLUGIN_ID); List<ReleaseInfo> movieReleaseInfo = new ArrayList<>(); MediaCreditList moviePeople = null; MovieInfo moviedb = null; boolean retval; // First look to see if we have a TMDb ID as this will make looking the film up easier if (StringTools.isValidString(tmdbID)) { // Search based on TMdb ID LOG.debug("{}: Using TMDb ID ({}) for {}", movie.getBaseName(), tmdbID, movie.getBaseName()); try { moviedb = tmdb.getMovieInfo(NumberUtils.toInt(tmdbID), languageCode); } catch (MovieDbException ex) { LOG.debug("{}: Failed to get movie info using TMDB ID: {} - {}", movie.getBaseName(), tmdbID, ex.getMessage()); } } if (moviedb == null && StringTools.isValidString(imdbID)) { // Search based on IMDb ID LOG.debug("{}: Using IMDb ID ({}) for {}", movie.getBaseName(), imdbID, movie.getBaseName()); try { moviedb = tmdb.getMovieInfoImdb(imdbID, languageCode); tmdbID = String.valueOf(moviedb.getId()); if (StringTools.isNotValidString(tmdbID)) { LOG.debug("{}: No TMDb ID found for movie!", movie.getBaseName()); } } catch (MovieDbException ex) { LOG.debug("{}: Failed to get movie info using IMDB ID: {} - {}", movie.getBaseName(), imdbID, ex.getMessage()); } } if (moviedb == null) { LOG.debug("{}: No IDs provided for movie, search using title & year", movie.getBaseName()); // Search using movie name int movieYear = NumberUtils.toInt(movie.getYear(), 0); // Check with the title LOG.debug(LOG_LOCATE_MOVIE_INFORMATION, movie.getBaseName(), movie.getTitle(), movieYear); moviedb = searchMovieTitle(movie, movieYear, movie.getTitle()); if (moviedb == null) { // Check with the original title LOG.debug(LOG_LOCATE_MOVIE_INFORMATION, movie.getBaseName(), movie.getOriginalTitle(), movieYear); moviedb = searchMovieTitle(movie, movieYear, movie.getOriginalTitle()); } // If still no matches try with a shorter title if (moviedb == null) { for (int words = 3; words > 0; words--) { String shortTitle = StringTools.getWords(movie.getTitle(), words); LOG.debug("{}: Using shorter title '{}'", movie.getBaseName(), shortTitle); moviedb = searchMovieTitle(movie, movieYear, shortTitle); if (moviedb != null) { LOG.debug("{}: Movie found", movie.getBaseName()); break; } } if (moviedb == null) { for (int words = 3; words > 0; words--) { String shortTitle = StringTools.getWords(movie.getOriginalTitle(), words); LOG.debug("{}: Using shorter title '{}'", movie.getBaseName(), shortTitle); moviedb = searchMovieTitle(movie, movieYear, shortTitle); if (moviedb != null) { LOG.debug("{}: Movie found", movie.getBaseName()); break; } } } } } if (moviedb == null) { LOG.debug("Movie {} not found!", movie.getBaseName()); LOG.debug("Try using a NFO file to specify the movie"); return false; } try { // Get the full information on the film moviedb = tmdb.getMovieInfo(moviedb.getId(), languageCode); } catch (MovieDbException ex) { LOG.debug("Failed to download remaining information for {}", movie.getBaseName()); } LOG.debug("Found id ({}) for {}", moviedb.getId(), moviedb.getTitle()); try { // Get the release information movieReleaseInfo = tmdb.getMovieReleaseInfo(moviedb.getId(), countryCode).getResults(); } catch (MovieDbException ex) { LOG.debug("Failed to get release information: {}", ex.getMessage(), ex); } try { // Get the cast information moviePeople = tmdb.getMovieCredits(moviedb.getId()); } catch (MovieDbException ex) { LOG.debug("Failed to get cast information: {}", ex.getMessage(), ex); } retval = true; if (moviedb.getId() > 0) { movie.setMovieType(Movie.TYPE_MOVIE); } if (StringTools.isValidString(moviedb.getTitle())) { copyMovieInfo(moviedb, movie); } // Set the release information if (!movieReleaseInfo.isEmpty() && OverrideTools.checkOverwriteCertification(movie, TMDB_PLUGIN_ID)) { // Default to the first one ReleaseInfo ri = movieReleaseInfo.get(0); for (ReleaseInfo release : movieReleaseInfo) { if (release.isPrimary()) { ri = release; break; } } LOG.trace("Using release information: {}", ri.toString()); movie.setCertification(ri.getCertification(), TMDB_PLUGIN_ID); } // Add the cast information // TODO: Add the people to the cast/crew if (moviePeople != null) { List<String> newActors = new ArrayList<>(); List<String> newDirectors = new ArrayList<>(); List<String> newWriters = new ArrayList<>(); LOG.debug("Adding {} people to the cast list", Math.min(moviePeople.getCast().size(), actorMax)); for (MediaCreditCast person : moviePeople.getCast()) { LOG.trace("Adding cast member {}", person.toString()); newActors.add(person.getName()); } LOG.debug("Adding {} people to the crew list", Math.min(moviePeople.getCrew().size(), 2)); for (MediaCreditCrew person : moviePeople.getCrew()) { LOG.trace("Adding crew member {}", person.toString()); if ("Directing".equalsIgnoreCase(person.getDepartment())) { LOG.trace("{} is a Director", person.getName()); newDirectors.add(person.getName()); } else if ("Writing".equalsIgnoreCase(person.getDepartment())) { LOG.trace("{} is a Writer", person.getName()); newWriters.add(person.getName()); } else { LOG.trace("Unknown job {} for {}", person.getJob(), person.toString()); } } if (OverrideTools.checkOverwriteDirectors(movie, TMDB_PLUGIN_ID)) { movie.setDirectors(newDirectors, TMDB_PLUGIN_ID); } if (OverrideTools.checkOverwritePeopleDirectors(movie, TMDB_PLUGIN_ID)) { movie.setPeopleDirectors(newDirectors, TMDB_PLUGIN_ID); } if (OverrideTools.checkOverwriteWriters(movie, TMDB_PLUGIN_ID)) { movie.setWriters(newWriters, TMDB_PLUGIN_ID); } if (OverrideTools.checkOverwritePeopleWriters(movie, TMDB_PLUGIN_ID)) { movie.setPeopleWriters(newWriters, TMDB_PLUGIN_ID); } if (OverrideTools.checkOverwriteActors(movie, TMDB_PLUGIN_ID)) { movie.setCast(newActors, TMDB_PLUGIN_ID); } if (OverrideTools.checkOverwritePeopleActors(movie, TMDB_PLUGIN_ID)) { movie.setPeopleCast(newActors, TMDB_PLUGIN_ID); } } else { LOG.debug("No cast or crew members found"); } // Update TheMovieDb Id if needed if (StringTools.isNotValidString(movie.getId(TMDB_PLUGIN_ID))) { movie.setId(TMDB_PLUGIN_ID, moviedb.getId()); } // Update IMDb Id if needed if (StringTools.isNotValidString(movie.getId(IMDB_PLUGIN_ID))) { movie.setId(IMDB_PLUGIN_ID, moviedb.getImdbID()); } // Create the auto sets for movies and not extras if (AUTO_COLLECTION && !movie.isExtra()) { Collection coll = moviedb.getBelongsToCollection(); if (coll != null) { LOG.debug("{} belongs to a collection: '{}'", movie.getTitle(), coll.getName()); CollectionInfo collInfo = getCollectionInfo(coll.getId(), languageCode); if (collInfo != null) { movie.addSet(collInfo.getName()); movie.setId(CACHE_COLLECTION, coll.getId()); } else { LOG.debug("Failed to get collection information!"); } } } if (downloadFanart && StringTools.isNotValidString(movie.getFanartURL())) { movie.setFanartURL(getFanartURL(movie)); if (StringTools.isValidString(movie.getFanartURL())) { movie.setFanartFilename(movie.getBaseName() + FANART_TOKEN + "." + fanartExtension); } } return retval; } /** * Search for a movie title. * * Use a title that may be different to the actual title of the movie, but match against the full title. * * @param fullTitle * @param year * @param searchTitle */ private MovieInfo searchMovieTitle(Movie movie, int movieYear, final String searchTitle) { LOG.debug(LOG_LOCATE_MOVIE_INFORMATION, movie.getBaseName(), searchTitle, movieYear); MovieInfo movieDb = null; ResultList<MovieInfo> result; try { result = tmdb.searchMovie(searchTitle, 0, languageCode, INCLUDE_ADULT, movieYear, null, SearchType.PHRASE); } catch (MovieDbException ex) { LOG.warn("Error scanning movie '{}': {}", movie.getTitle(), ex.getMessage(), ex); return movieDb; } LOG.debug("{}: Found {} potential matches", movie.getBaseName(), result.getResults().size()); List<MovieInfo> movieList = result.getResults(); // Are the title and original title the same (used for performance) boolean sameTitle = StringUtils.equalsIgnoreCase(movie.getTitle(), movie.getOriginalTitle()); // Iterate over the list until we find a match for (MovieInfo movieInfo : movieList) { String movieInfoYear = StringUtils.isBlank(movieInfo.getReleaseDate()) ? "UNKNOWN" : movieInfo.getReleaseDate().substring(0, 4); LOG.debug("Checking {} ({})", movieInfo.getTitle(), movieInfoYear); if (Compare.movies(movieInfo, movie.getTitle(), movieYear > 0 ? String.valueOf(movieYear) : "", SEARCH_MATCH, false)) { LOG.debug("Matched to '{}'", movie.getTitle()); movieDb = movieInfo; break; } else if (!sameTitle && Compare.movies(movieInfo, movie.getOriginalTitle(), String.valueOf(movieYear), SEARCH_MATCH, false)) { // See if the original title is different and then compare it too LOG.debug("Matched to '{}'", movie.getOriginalTitle()); movieDb = movieInfo; break; } } return movieDb; } /** * Copy the movie info from the MovieDB bean to the YAMJ movie bean * * @param moviedb The MovieDB source * @param movie The YAMJ target * @return The altered movie bean */ private static void copyMovieInfo(MovieInfo moviedb, Movie movie) { // TMDb ID movie.setId(TMDB_PLUGIN_ID, moviedb.getId()); // IMDb ID movie.setId(IMDB_PLUGIN_ID, moviedb.getImdbID()); // title if (OverrideTools.checkOverwriteTitle(movie, TMDB_PLUGIN_ID)) { movie.setTitle(moviedb.getTitle(), TMDB_PLUGIN_ID); } // original title if (OverrideTools.checkOverwriteOriginalTitle(movie, TMDB_PLUGIN_ID)) { movie.setOriginalTitle(moviedb.getOriginalTitle(), TMDB_PLUGIN_ID); } // plot if (OverrideTools.checkOverwritePlot(movie, TMDB_PLUGIN_ID)) { movie.setPlot(moviedb.getOverview(), TMDB_PLUGIN_ID); } // outline if (OverrideTools.checkOverwriteOutline(movie, TMDB_PLUGIN_ID)) { movie.setOutline(moviedb.getOverview(), TMDB_PLUGIN_ID); } // rating if (overwriteCheck(String.valueOf(moviedb.getVoteAverage()), String.valueOf(movie.getRating()))) { try { float rating = moviedb.getVoteAverage() * 10; // Convert rating to integer movie.addRating(TMDB_PLUGIN_ID, (int) rating); } catch (Exception error) { LOG.debug("Error converting rating for {}", movie.getBaseName()); } } // release date if (OverrideTools.checkOverwriteReleaseDate(movie, TMDB_PLUGIN_ID)) { movie.setReleaseDate(moviedb.getReleaseDate(), TMDB_PLUGIN_ID); } // year if (OverrideTools.checkOverwriteYear(movie, TMDB_PLUGIN_ID)) { String year = moviedb.getReleaseDate(); // Check if this is the default year and skip it if (StringUtils.isNotBlank(year) && !"1900-01-01".equals(year)) { year = (new DateTime(year)).toString("yyyy"); movie.setYear(year, TMDB_PLUGIN_ID); } } // runtime if (OverrideTools.checkOverwriteRuntime(movie, TMDB_PLUGIN_ID)) { movie.setRuntime(String.valueOf(moviedb.getRuntime()), TMDB_PLUGIN_ID); } // tagline if (OverrideTools.checkOverwriteTagline(movie, TMDB_PLUGIN_ID)) { movie.setTagline(moviedb.getTagline(), TMDB_PLUGIN_ID); } // Country if (OverrideTools.checkOverwriteCountry(movie, TMDB_PLUGIN_ID)) { List<String> countries = new ArrayList<>(); for (ProductionCountry productionCountry : moviedb.getProductionCountries()) { countries.add(productionCountry.getName()); } movie.setCountries(countries, TMDB_PLUGIN_ID); } // Company if (OverrideTools.checkOverwriteCompany(movie, TMDB_PLUGIN_ID)) { List<ProductionCompany> studios = moviedb.getProductionCompanies(); if (!studios.isEmpty()) { String studio = studios.get(0).getName(); movie.setCompany(studio, TMDB_PLUGIN_ID); } } // Language if (!moviedb.getSpokenLanguages().isEmpty() && OverrideTools.checkOverwriteLanguage(movie, TMDB_PLUGIN_ID)) { StringBuilder movieLanguage = new StringBuilder(); String langCode = moviedb.getSpokenLanguages().get(0).getCode(); if (StringTools.isValidString(langCode)) { movieLanguage.append(MovieFilenameScanner.determineLanguage(langCode)); } if (moviedb.getSpokenLanguages().size() > 1) { for (Language lang : moviedb.getSpokenLanguages()) { if (movieLanguage.length() > 0) { movieLanguage.append(LANGUAGE_DELIMITER); } movieLanguage.append(MovieFilenameScanner.determineLanguage(lang.getCode())); } } movie.setLanguage(movieLanguage.toString(), TMDB_PLUGIN_ID); } // Genres if (OverrideTools.checkOverwriteGenres(movie, TMDB_PLUGIN_ID)) { List<String> newGenres = new ArrayList<>(); for (Genre genre : moviedb.getGenres()) { newGenres.add(genre.getName()); } movie.setGenres(newGenres, TMDB_PLUGIN_ID); } } /** * Checks to see if the source string is null or "UNKNOWN" and that target string ISN'T null or "UNKNOWN" * * @param sourceString The source string to check * @param targetString The destination string to check * @return True if valid to overwrite */ private static boolean overwriteCheck(String sourceString, String targetString) { return StringTools.isValidString(sourceString) && (StringTools.isNotValidString(targetString) || "-1".equals(targetString)); } @Override public boolean scanNFO(String nfo, Movie movie) { int beginIndex; boolean result = Boolean.FALSE; if (StringTools.isValidString(movie.getId(TMDB_PLUGIN_ID))) { LOG.debug("TheMovieDb ID exists for {} = {}", movie.getBaseName(), movie.getId(TMDB_PLUGIN_ID)); result = Boolean.TRUE; } else { LOG.debug("Scanning NFO for TheMovieDb ID"); beginIndex = nfo.indexOf("/movie/"); if (beginIndex != -1) { StringTokenizer st = new StringTokenizer(nfo.substring(beginIndex + 7), TOKEN_SPLIT); movie.setId(TMDB_PLUGIN_ID, st.nextToken()); LOG.debug("TheMovieDb ID found in NFO = {}", movie.getId(TMDB_PLUGIN_ID)); result = Boolean.TRUE; } else { LOG.debug("No TheMovieDb ID found in NFO!"); } } // We might as well look for the IMDb ID as well if (StringTools.isValidString(movie.getId(IMDB_PLUGIN_ID))) { LOG.debug("IMDB ID exists for {} = {}", movie.getBaseName(), movie.getId(IMDB_PLUGIN_ID)); result = Boolean.TRUE; } else { beginIndex = nfo.indexOf("/tt"); if (beginIndex != -1) { StringTokenizer st = new StringTokenizer(nfo.substring(beginIndex + 1), TOKEN_SPLIT); movie.setId(ImdbPlugin.IMDB_PLUGIN_ID, StringUtils.trim(st.nextToken())); LOG.debug("IMDB ID found in nfo = {}", movie.getId(ImdbPlugin.IMDB_PLUGIN_ID)); } else { beginIndex = nfo.indexOf("/Title?"); if (beginIndex != -1 && beginIndex + 7 < nfo.length()) { StringTokenizer st = new StringTokenizer(nfo.substring(beginIndex + 7), TOKEN_SPLIT); movie.setId(ImdbPlugin.IMDB_PLUGIN_ID, "tt" + StringUtils.trim(st.nextToken())); LOG.debug("IMDB ID found in NFO = {}", movie.getId(ImdbPlugin.IMDB_PLUGIN_ID)); } } } return result; } @Override public void scanTVShowTitles(Movie movie) { // TheMovieDB.org does not have any TV Shows (yet), so just return } /** * Locate the FanartURL for the movie. This should probably be skipped as this uses TheMovieDb.org anyway * * @param movie Movie bean for the movie to locate * @return The URL of the fanart */ protected String getFanartURL(Movie movie) { return FanartScanner.getFanartURL(movie); } /** * Search for information on the person * * @param person * @return */ @Override public boolean scan(Person person) { String id = getPersonId(person); if (StringTools.isValidString(id)) { int tmdbId = Integer.parseInt(id); try { PersonInfo tmdbPerson = tmdb.getPersonInfo(tmdbId); LOG.info(tmdbPerson.toString()); if (skipFaceless && StringUtils.isBlank(tmdbPerson.getProfilePath())) { LOG.debug("Skipped '{}' no profile picture found (skip.faceless=true)", tmdbPerson.getName()); return Boolean.FALSE; } person.setName(tmdbPerson.getName()); person.setBiography(StringUtils.abbreviate(tmdbPerson.getBiography(), preferredBiographyLength)); person.setId(IMDB_PLUGIN_ID, tmdbPerson.getImdbId()); person.setUrl("http://www.themoviedb.org/person/" + tmdbId); List<String> akas = tmdbPerson.getAlsoKnownAs(); if (akas != null && !akas.isEmpty()) { // Set the birthname to be the first aka person.setBirthName(akas.get(0)); // Add the akas for (String aka : tmdbPerson.getAlsoKnownAs()) { person.addAka(aka); } } if (StringTools.isValidString(tmdbPerson.getBirthday())) { // Look for the death day and append that as well if (StringTools.isValidString(tmdbPerson.getDeathday())) { person.setYear(tmdbPerson.getBirthday() + "/" + tmdbPerson.getDeathday()); } else { person.setYear(tmdbPerson.getBirthday()); } } person.setBirthPlace(tmdbPerson.getPlaceOfBirth()); URL url = tmdb.createImageUrl(tmdbPerson.getProfilePath(), ORIGINAL); person.setPhotoURL(url.toString()); person.setPhotoFilename(); // Filmography PersonCreditList<CreditMovieBasic> results = tmdb.getPersonMovieCredits(tmdbId, languageCode); person.setKnownMovies(results.getCast().size() + results.getCrew().size()); int actorCount = 0; int directorCount = 0; int writerCount = 0; // Process the credits into a filmography list List<Filmography> filmList = new ArrayList<>(); // Process the cast if (jobsInclude.contains("Actor")) { for (CreditMovieBasic credit : results.getCast()) { if (++actorCount > actorMax) { // Over the limit, so stop break; } LOG.debug("Credit job: {} = '{}' - {} ({})", credit.getCreditType(), credit.getJob(), credit.getTitle(), credit.getReleaseDate()); filmList.add(convertMovieCredit(credit)); } } // Process the crew for (CreditMovieBasic credit : results.getCrew()) { if (jobsInclude.contains("Director") && (++directorCount > directorMax)) { // Skip this record as no more are needed LOG.debug("Skipping DIRECTOR '{}' (max reached)", person.getName()); continue; } else if (jobsInclude.contains("Writer") && (++writerCount > writerMax)) { // Skip this record as no more are needed LOG.debug("Skipping WRITER '{}' (max reached)", person.getName()); continue; } LOG.debug("Credit job: {} = '{}' - {} ({})", credit.getCreditType(), credit.getJob(), credit.getTitle(), credit.getReleaseDate()); filmList.add(convertMovieCredit(credit)); } LOG.debug("Actors found : {}, max: {}", actorCount, actorMax); LOG.debug("Directors found: {}, max: {}", directorCount, directorMax); LOG.debug("Writers found : {}, max: {}", writerCount, writerMax); // See if we need to trim the list if (filmList.size() > preferredFilmographyMax) { LOG.debug("Reached limit of {} films for {}. Total found: {}", preferredFilmographyMax, person.getName(), filmList.size()); // Sort the collection to ensure the most relevant films are at the start Collections.sort(filmList, filmographyCmp); person.setFilmography(filmList.subList(0, preferredFilmographyMax)); } else { person.setFilmography(filmList); } // Update the version int version = person.getVersion(); person.setVersion(++version); return Boolean.TRUE; } catch (MovieDbException ex) { LOG.warn("Failed to get information on {} ({}), error: {}", person.getName(), tmdbId, ex.getMessage(), ex); return Boolean.FALSE; } } return Boolean.FALSE; } private static Filmography convertMovieCredit(CreditMovieBasic credit) { Filmography film = new Filmography(); film.setId(TMDB_PLUGIN_ID, Integer.toString(credit.getId())); film.setName(credit.getTitle()); film.setOriginalTitle(credit.getOriginalTitle()); film.setYear(credit.getReleaseDate()); film.setJob(credit.getJob()); film.setCharacter(credit.getCharacter()); film.setDepartment(credit.getDepartment()); film.setUrl("www.themoviedb.org/movie/" + credit.getId()); return film; } /** * Locate and return the TMDB person ID from the person object * * @param person * @return */ public String getPersonId(com.moviejukebox.model.Person person) { String tmdbId = person.getId(TMDB_PLUGIN_ID); if (StringTools.isNotValidString(tmdbId) && StringTools.isValidString(person.getName())) { // Look for the ID using the person's name tmdbId = getPersonId(person.getName()); if (StringTools.isValidString(tmdbId)) { LOG.info("{}: ID found '{}'", person.getName(), tmdbId); person.setId(TMDB_PLUGIN_ID, tmdbId); } else { LOG.warn("{}: No ID found", person.getName()); } } return tmdbId; } /** * Attempt to find the TMDB ID for a person using their full name * * @param name * @return */ public String getPersonId(String name) { String tmdbId = ""; PersonFind closestPerson = null; int closestMatch = Integer.MAX_VALUE; boolean foundPerson = Boolean.FALSE; boolean includeAdult = PropertiesUtil.getBooleanProperty("themoviedb.includeAdult", Boolean.FALSE); try { ResultList<PersonFind> results = tmdb.searchPeople(name, 0, includeAdult, SearchType.PHRASE); LOG.info("Found {} person results for {}", results.getResults().size(), name); for (PersonFind person : results.getResults()) { if (name.equalsIgnoreCase(person.getName())) { tmdbId = String.valueOf(person.getId()); foundPerson = Boolean.TRUE; break; } LOG.trace("Checking {} against {}", name, person.getName()); int lhDistance = StringUtils.getLevenshteinDistance(name, person.getName()); LOG.trace("{}: Current closest match is {}, this match is {}", name, closestMatch, lhDistance); if (lhDistance < closestMatch) { LOG.trace("{}: TMDB ID {} is a better match", name, person.getId()); closestMatch = lhDistance; closestPerson = person; } } if (foundPerson) { LOG.debug("{}: Matched against TMDB ID: {}", name, tmdbId); } else if (closestMatch < Integer.MAX_VALUE && closestPerson != null) { tmdbId = String.valueOf(closestPerson.getId()); LOG.debug("{}: Closest match is '{}' differing by {} characters", name, closestPerson.getName(), closestMatch); } else { LOG.debug("{}: No match found", name); } } catch (MovieDbException ex) { LOG.warn("Failed to get information on '{}', error: {}", name, ex.getMessage(), ex); } return tmdbId; } /** * Get the Collection information from the cache or online * * @param collectionId * @return */ public CollectionInfo getCollectionInfo(int collectionId) { return getCollectionInfo(collectionId, languageCode); } /** * Get the Collection information from the cache or online * * @param collectionId * @param languageCode * @return */ public CollectionInfo getCollectionInfo(int collectionId, String languageCode) { String cacheKey = getCollectionCacheKey(collectionId, languageCode); CollectionInfo collInfo = (CollectionInfo) CacheMemory.getFromCache(cacheKey); if (collInfo == null) { // Not found in cache, so look online try { collInfo = tmdb.getCollectionInfo(collectionId, languageCode); if (collInfo != null) { URL newUrl; // Update the URL to be the full URL if (collInfo.getPosterPath() != null) { newUrl = tmdb.createImageUrl(collInfo.getPosterPath(), ORIGINAL); collInfo.setPosterPath(newUrl.toString()); } // Update the URL to be the full URL if (collInfo.getBackdropPath() != null) { newUrl = tmdb.createImageUrl(collInfo.getBackdropPath(), ORIGINAL); collInfo.setBackdropPath(newUrl.toString()); } // Add to the cache CacheMemory.addToCache(cacheKey, collInfo); } } catch (MovieDbException error) { LOG.warn("Error getting CollectionInfo: {}", error.getMessage()); } } return collInfo; } /** * Get the poster for a collection using the default language * * @param collectionId * @return */ public String getCollectionPoster(int collectionId) { return getCollectionPoster(collectionId, languageCode); } /** * Get the poster for a collection * * @param collectionId * @param languageCode * @return */ public String getCollectionPoster(int collectionId, String languageCode) { return getCollectionImage(collectionId, ArtworkType.POSTER, languageCode); } /** * Get the fanart for a collection using the default language * * @param collectionId * @return */ public String getCollectionFanart(int collectionId) { return getCollectionFanart(collectionId, languageCode); } /** * Get the fanart for a collection * * @param collectionId * @param languageCode * @return */ public String getCollectionFanart(int collectionId, String languageCode) { return getCollectionImage(collectionId, ArtworkType.BACKDROP, languageCode); } /** * Generic method to get the artwork for a collection. * * @param collectionId * @param artworkType * @param languageCode * @return */ private String getCollectionImage(int collectionId, ArtworkType artworkType, String languageCode) { String returnUrl = Movie.UNKNOWN; String cacheKey = getCollectionImagesCacheKey(collectionId, languageCode); LOG.debug("Getting {} for collection ID {}, language '{}'", artworkType, collectionId, languageCode); @SuppressWarnings("unchecked") List<Artwork> results = (ArrayList<Artwork>) CacheMemory.getFromCache(cacheKey); if (results == null) { try { // Pass the language as null so that we get all images returned, even those without a language. ResultList<Artwork> collResults = tmdb.getCollectionImages(collectionId, null); if (collResults != null && collResults.getResults() != null && !collResults.getResults().isEmpty()) { results = new ArrayList<>(collResults.getResults()); // Add to the cache CacheMemory.addToCache(cacheKey, results); } else { LOG.debug("No results found for {}-{}", collectionId, languageCode); } } catch (MovieDbException error) { LOG.warn("Error getting CollectionImages: {}", error.getMessage()); } } // Check we got some results if (results != null && !results.isEmpty()) { // Loop over the results looking for the required artwork type for (Artwork artwork : results) { if (artwork.getArtworkType() == artworkType && (StringUtils.isBlank(artwork.getLanguage()) || artwork.getLanguage().equalsIgnoreCase(languageCode))) { try { // We have a match, update the URL with the full path URL url = tmdb.createImageUrl(artwork.getFilePath(), ORIGINAL); returnUrl = url.toString(); // Stop processing now. break; } catch (MovieDbException ex) { LOG.warn("Failed to get URL for {}, error: {}", artworkType, ex.getMessage(), ex); } } } } return returnUrl; } /** * Get a cache key for the collection * * @param collectionId * @param languageCode * @return */ public static String getCollectionCacheKey(int collectionId, String languageCode) { return getCacheKey(CACHE_COLLECTION, Integer.toString(collectionId), languageCode); } /** * Get a cache key for the collection images * * @param collectionId * @param languageCode * @return */ public static String getCollectionImagesCacheKey(int collectionId, String languageCode) { return getCacheKey(CACHE_COLLECTION_IMAGES, Integer.toString(collectionId), languageCode); } /** * Get a cache key * * @param collectionId * @return */ private static String getCacheKey(String cacheType, String collectionId, String languageCode) { return CacheMemory.generateCacheKey(cacheType, collectionId, languageCode); } }