Java tutorial
/* * Copyright (c) 2004-2013 YAMJ Members * https://github.com/organizations/YAMJ/teams * * This file is part of the Yet Another Media Jukebox (YAMJ). * * 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-v3 * */ package org.yamj.core.service.plugin; import com.omertron.themoviedbapi.MovieDbException; import com.omertron.themoviedbapi.TheMovieDbApi; import com.omertron.themoviedbapi.model.MovieDb; import com.omertron.themoviedbapi.model.PersonType; import com.omertron.themoviedbapi.model.ProductionCountry; import com.omertron.themoviedbapi.results.TmdbResultsList; import java.text.ParseException; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.yamj.core.configuration.ConfigService; import org.yamj.core.database.model.Person; import org.yamj.core.database.model.VideoData; import org.yamj.core.database.model.dto.CreditDTO; import org.yamj.core.database.model.type.JobType; import org.yamj.core.tools.OverrideTools; @Service("tmdbScanner") public class TheMovieDbScanner implements IMovieScanner, IPersonScanner, InitializingBean { public static final String SCANNER_ID = "tmdb"; private static final Logger LOG = LoggerFactory.getLogger(TheMovieDbScanner.class); private static final String FROM_WIKIPEDIA = "From Wikipedia, the free encyclopedia"; private static final String WIKIPEDIA_DESCRIPTION_ABOVE = "Description above from the Wikipedia"; @Autowired private PluginMetadataService pluginMetadataService; @Autowired private ConfigService configService; @Autowired private TheMovieDbApi tmdbApi; @Override public String getScannerName() { return SCANNER_ID; } @Override public void afterPropertiesSet() { // register this scanner pluginMetadataService.registerMovieScanner(this); pluginMetadataService.registerPersonScanner(this); } @Override public String getMovieId(VideoData videoData) { String tmdbID = videoData.getSourceDbId(SCANNER_ID); String imdbID = videoData.getSourceDbId(ImdbScanner.SCANNER_ID); String defaultLanguage = configService.getProperty("themoviedb.language", "en"); MovieDb moviedb = null; // First look to see if we have a TMDb ID as this will make looking the film up easier if (StringUtils.isNotBlank(tmdbID)) { // Search based on TMdb ID LOG.debug("Using TMDb ID ({}) for {}", tmdbID, videoData.getTitle()); try { moviedb = tmdbApi.getMovieInfo(Integer.parseInt(tmdbID), defaultLanguage); } catch (MovieDbException ex) { LOG.debug("Failed to get movie info using TMDB ID: {}, Error: {}", tmdbID, ex.getMessage()); moviedb = null; } } if (moviedb == null && StringUtils.isNotBlank(imdbID)) { // Search based on IMDb ID LOG.debug("Using IMDb ID ({}) for {}", imdbID, videoData.getTitle()); try { moviedb = tmdbApi.getMovieInfoImdb(imdbID, defaultLanguage); tmdbID = String.valueOf(moviedb.getId()); if (StringUtils.isBlank(tmdbID)) { LOG.debug("Failed to get movie info using IMDB ID {} for {}", imdbID, videoData.getTitle()); } } catch (MovieDbException ex) { LOG.debug("Failed to get movie info using IMDB ID: {}, Error: {}", imdbID, ex.getMessage()); moviedb = null; } } if (moviedb == null && StringUtils.isNotBlank(videoData.getTitle())) { LOG.debug("No ID found for {}, trying title search with '{} ({})'", videoData.getTitle(), videoData.getTitle(), videoData.getPublicationYear()); tmdbID = getMovieId(videoData.getTitle(), videoData.getPublicationYear()); } if (StringUtils.isNotBlank(tmdbID)) { LOG.info("Found TMDB ID: {}", tmdbID); videoData.setSourceDbId(SCANNER_ID, tmdbID); } else { LOG.info("No TMDB ID found for ", videoData.getTitle()); } return tmdbID; } @Override public String getMovieId(String title, int year) { MovieDb moviedb = null; String defaultLanguage = configService.getProperty("themoviedb.language", "en"); boolean includeAdult = configService.getBooleanProperty("themoviedb.includeAdult", Boolean.FALSE); int searchMatch = configService.getIntProperty("themoviedb.searchMatch", 3); try { // Search using movie name List<MovieDb> movieList = tmdbApi.searchMovie(title, year, defaultLanguage, includeAdult, 0) .getResults(); LOG.info("Found {} potential matches for {} ({})", movieList.size(), title, year); // Iterate over the list until we find a match for (MovieDb m : movieList) { String relDate; if (StringUtils.isNotBlank(m.getReleaseDate()) && m.getReleaseDate().length() > 4) { relDate = m.getReleaseDate().substring(0, 4); } else { relDate = ""; } LOG.debug("Checking " + m.getTitle() + " (" + relDate + ")"); if (TheMovieDbApi.compareMovies(m, title, String.valueOf(year), searchMatch)) { moviedb = m; break; } // See if the original title is different and then compare it too // if (!movie.getTitle().equals(movie.getOriginalTitle()) // && TheMovieDbApi.compareMovies(m, movie.getOriginalTitle(), Integer.toString(movieYear))) { // moviedb = m; // break; // } } } catch (MovieDbException ex) { LOG.debug("Failed to get movie info for {}, error: {}", title, ex.getMessage()); moviedb = null; } if (moviedb != null) { LOG.info("TMDB ID found {} for {}", moviedb.getId(), title); return String.valueOf(moviedb.getId()); } return ""; } @Override public ScanResult scan(VideoData videoData) { String tmdbID = getMovieId(videoData); if (StringUtils.isBlank(tmdbID)) { LOG.debug("Missing TMDB ID for {}", videoData.getTitle()); return ScanResult.MISSING_ID; } return updateVideoData(videoData); } private ScanResult updateVideoData(VideoData videoData) { String tmdbID = videoData.getSourceDbId(SCANNER_ID); String defaultLanguage = configService.getProperty("themoviedb.language", "en"); MovieDb moviedb; if (StringUtils.isBlank(tmdbID)) { LOG.error("Failed retrieving TheMovieDb information for {}, missing id.", videoData.getTitle()); return ScanResult.MISSING_ID; } LOG.info("Getting information for TMDB ID:{}-{}", tmdbID, videoData.getTitle()); try { moviedb = tmdbApi.getMovieInfo(Integer.parseInt(tmdbID), defaultLanguage); } catch (MovieDbException ex) { LOG.error("Failed retrieving TheMovieDb information for {}, error: {}", videoData.getTitle(), ex.getMessage()); return ScanResult.ERROR; } if (OverrideTools.checkOverwriteTitle(videoData, SCANNER_ID)) { videoData.setTitle(StringUtils.trim(moviedb.getTitle()), SCANNER_ID); } if (OverrideTools.checkOverwritePlot(videoData, SCANNER_ID)) { videoData.setPlot(StringUtils.trim(moviedb.getOverview()), SCANNER_ID); } if (OverrideTools.checkOverwriteOutline(videoData, SCANNER_ID)) { videoData.setOutline(StringUtils.trim(moviedb.getOverview()), SCANNER_ID); } if (OverrideTools.checkOverwriteCountry(videoData, SCANNER_ID)) { for (ProductionCountry country : moviedb.getProductionCountries()) { videoData.setCountry(StringUtils.trimToNull(country.getName()), SCANNER_ID); break; } } if (OverrideTools.checkOverwriteYear(videoData, SCANNER_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"); videoData.setPublicationYear(Integer.parseInt(year), SCANNER_ID); } } if (OverrideTools.checkOverwriteGenres(videoData, SCANNER_ID)) { // GENRES Set<String> genreNames = new HashSet<String>(); for (com.omertron.themoviedbapi.model.Genre genre : moviedb.getGenres()) { genreNames.add(StringUtils.trim(genre.getName())); } videoData.setGenreNames(genreNames, SCANNER_ID); } // CAST & CREW try { CreditDTO credit; for (com.omertron.themoviedbapi.model.Person person : tmdbApi.getMovieCasts(Integer.parseInt(tmdbID)) .getResults()) { credit = new CreditDTO(); credit.setSourcedb(SCANNER_ID); credit.setSourcedbId(String.valueOf(person.getId())); credit.setName(StringUtils.trim(person.getName())); credit.setRole(StringUtils.trimToNull(person.getCharacter())); if (person.getAka() != null && !person.getAka().isEmpty()) { credit.setAka(StringUtils.trimToNull(person.getAka().get(0))); } if (person.getPersonType() == PersonType.CAST) { credit.setJobType(JobType.ACTOR); } else if (person.getPersonType() == PersonType.CREW) { if (person.getDepartment().equalsIgnoreCase("writing")) { credit.setJobType(JobType.WRITER); } else if (person.getDepartment().equalsIgnoreCase("directing")) { credit.setJobType(JobType.DIRECTOR); } else if (person.getDepartment().equalsIgnoreCase("production")) { credit.setJobType(JobType.PRODUCER); } else if (person.getDepartment().equalsIgnoreCase("sound")) { credit.setJobType(JobType.SOUND); } else if (person.getDepartment().equalsIgnoreCase("camera")) { credit.setJobType(JobType.CAMERA); } else if (person.getDepartment().equalsIgnoreCase("art")) { credit.setJobType(JobType.ART); } else if (person.getDepartment().equalsIgnoreCase("editing")) { credit.setJobType(JobType.EDITING); } else if (person.getDepartment().equalsIgnoreCase("costume & make-up")) { credit.setJobType(JobType.COSTUME_MAKEUP); } else if (person.getDepartment().equalsIgnoreCase("crew")) { credit.setJobType(JobType.CREW); } else if (person.getDepartment().equalsIgnoreCase("visual effects")) { credit.setJobType(JobType.EFFECTS); } else if (person.getDepartment().equalsIgnoreCase("lighting")) { credit.setJobType(JobType.LIGHTING); } else { LOG.debug("Adding unknown department '{}' for: '{}', person: '{}'", person.getDepartment(), videoData.getTitle(), person.getName()); LOG.trace("Person: {}", person.toString()); credit.setJobType(JobType.UNKNOWN); } } else { LOG.debug("Unknown job type: '{}', for: '{}', person: '{}'", person.getPersonType().toString(), videoData.getTitle(), person.getName()); LOG.trace("Person: {}", person.toString()); } videoData.addCreditDTO(credit); } } catch (MovieDbException ex) { LOG.error("Error getting cast from TheMovieDB: {}", ex.getMessage()); } return ScanResult.OK; } @Override public String getPersonId(Person person) { String id = person.getPersonId(SCANNER_ID); if (StringUtils.isNotBlank(id)) { return id; } else if (StringUtils.isNotBlank(person.getName())) { return getPersonId(person.getName()); } else { LOG.error("No ID or Name found for {}", person.toString()); return ""; } } @Override public String getPersonId(String name) { String id = ""; com.omertron.themoviedbapi.model.Person closestPerson = null; int closestMatch = Integer.MAX_VALUE; boolean foundPerson = Boolean.FALSE; boolean includeAdult = configService.getBooleanProperty("themoviedb.includeAdult", Boolean.FALSE); try { TmdbResultsList<com.omertron.themoviedbapi.model.Person> results = tmdbApi.searchPeople(name, includeAdult, 0); LOG.info("{}: Found {} results", name, results.getResults().size()); for (com.omertron.themoviedbapi.model.Person person : results.getResults()) { if (name.equalsIgnoreCase(person.getName())) { id = String.valueOf(person.getId()); foundPerson = Boolean.TRUE; break; } else { 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, id); } else if (closestMatch < Integer.MAX_VALUE && closestPerson != null) { id = 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 '{}' from {}, error: {}", name, SCANNER_ID, ex.getMessage()); } return id; } @Override public ScanResult scan(Person person) { String id = getPersonId(person); if (StringUtils.isBlank(id) || !StringUtils.isNumeric(id)) { return ScanResult.MISSING_ID; } try { LOG.debug("Getting information on {}-'{}' from {}", person.getId(), person.getName(), SCANNER_ID); com.omertron.themoviedbapi.model.Person tmdbPerson = tmdbApi.getPersonInfo(Integer.parseInt(id)); person.setBiography(cleanBiography(tmdbPerson.getBiography())); person.setBirthPlace(StringUtils.trimToNull(tmdbPerson.getBirthplace())); person.setPersonId(ImdbScanner.SCANNER_ID, StringUtils.trim(tmdbPerson.getImdbId())); Date parsedDate = parseDate(tmdbPerson.getBirthday()); if (parsedDate != null) { person.setBirthDay(parsedDate); } parsedDate = parseDate(tmdbPerson.getDeathday()); if (parsedDate != null) { person.setDeathDay(parsedDate); } } catch (MovieDbException ex) { LOG.warn("Failed to get information on {}-'{}', error: {}", id, person.getName(), ex.getMessage()); return ScanResult.ERROR; } LOG.debug("Successfully processed person: {}-'{}'", id, person.getName()); return ScanResult.OK; } /** * Convert string to date * * if the date is just a year, "-01-01" (1st Jan) will be appended to the date for conversion purposes * * @param dateToConvert * @return */ private Date parseDate(final String dateToConvert) { String dtc = StringUtils.trimToEmpty(dateToConvert); if (StringUtils.isNotBlank(dtc)) { try { if (dtc.length() == 4) { // Looks like just the year, so add a default to it for processing. dtc += "-01-01"; } return DateUtils.parseDate(dtc, "yyyy-MM-dd"); } catch (ParseException ex) { LOG.warn("Failed to convert date '{}'", dtc); } } return null; } /** * Remove unneeded text from the biography * * @param bio * @return */ private String cleanBiography(final String bio) { String newBio = StringUtils.trimToNull(bio); if (newBio == null) { return null; } newBio = newBio.replaceAll("\\s+", " "); int pos = StringUtils.indexOfIgnoreCase(newBio, FROM_WIKIPEDIA); if (pos >= 0) { // We've found the text, so remove it LOG.trace("Removing start wikipedia text from bio"); newBio = newBio.substring(pos + FROM_WIKIPEDIA.length() + 1); } pos = StringUtils.indexOfIgnoreCase(newBio, WIKIPEDIA_DESCRIPTION_ABOVE); if (pos >= 0) { LOG.trace("Removing end wikipedia text from bio"); newBio = newBio.substring(0, pos); } return newBio.trim(); } }