org.yamj.core.service.metadata.online.TheMovieDbScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.yamj.core.service.metadata.online.TheMovieDbScanner.java

Source

/*
 *      Copyright (c) 2004-2015 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.metadata.online;

import com.omertron.themoviedbapi.model.credits.CreditBasic;
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.person.PersonCreditList;
import com.omertron.themoviedbapi.model.person.PersonInfo;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringTokenizer;
import javax.annotation.PostConstruct;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.yamj.core.configuration.ConfigServiceWrapper;
import org.yamj.core.database.model.FilmParticipation;
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.database.model.type.ParticipationType;
import org.yamj.core.service.metadata.nfo.InfoDTO;
import org.yamj.core.tools.MetadataTools;
import org.yamj.core.tools.OverrideTools;
import org.yamj.core.tools.PersonNameDTO;
import org.yamj.core.tools.web.TemporaryUnavailableException;

@Service("tmdbScanner")
public class TheMovieDbScanner implements IMovieScanner, IFilmographyScanner {

    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 OnlineScannerService onlineScannerService;
    @Autowired
    private ConfigServiceWrapper configServiceWrapper;
    @Autowired
    private TheMovieDbApiWrapper tmdbApiWrapper;

    @Override
    public String getScannerName() {
        return SCANNER_ID;
    }

    @PostConstruct
    public void init() {
        LOG.info("Initialize TheMovieDb scanner");

        // register this scanner
        onlineScannerService.registerMovieScanner(this);
        onlineScannerService.registerPersonScanner(this);
    }

    @Override
    public String getMovieId(VideoData videoData) {
        return getMovieId(videoData, false);
    }

    private String getMovieId(VideoData videoData, boolean throwTempError) {
        String tmdbId = videoData.getSourceDbId(SCANNER_ID);
        if (StringUtils.isNumeric(tmdbId)) {
            return tmdbId;
        }

        String imdbId = videoData.getSourceDbId(ImdbScanner.SCANNER_ID);
        if (StringUtils.isNotBlank(imdbId)) {
            // Search based on IMDb ID
            LOG.debug("Using IMDb id {} for '{}'", imdbId, videoData.getTitle());
            MovieInfo movieDb = tmdbApiWrapper.getMovieInfoByIMDB(imdbId, throwTempError);
            if (movieDb != null && movieDb.getId() != 0) {
                tmdbId = String.valueOf(movieDb.getId());
            }
        }

        if (!StringUtils.isNumeric(tmdbId)) {
            LOG.debug("No TMDb id found for '{}', searching title with year {}", videoData.getTitle(),
                    videoData.getPublicationYear());
            tmdbId = tmdbApiWrapper.getMovieDbId(videoData.getTitle(), videoData.getPublicationYear(),
                    throwTempError);
            videoData.setSourceDbId(SCANNER_ID, tmdbId);
        }

        if (!StringUtils.isNumeric(tmdbId) && StringUtils.isNotBlank(videoData.getTitleOriginal())) {
            LOG.debug("No TMDb id found for '{}', searching original title with year {}",
                    videoData.getTitleOriginal(), videoData.getPublicationYear());
            tmdbId = tmdbApiWrapper.getMovieDbId(videoData.getTitleOriginal(), videoData.getPublicationYear(),
                    throwTempError);
            videoData.setSourceDbId(SCANNER_ID, tmdbId);
        }

        if (StringUtils.isNumeric(tmdbId)) {
            videoData.setSourceDbId(SCANNER_ID, tmdbId);
            return tmdbId;
        }

        return null;
    }

    @Override
    public ScanResult scan(VideoData videoData) {
        MovieInfo movieDb = null;
        MediaCreditList movieCasts = null;
        try {
            boolean throwTempError = configServiceWrapper
                    .getBooleanProperty("themoviedb.throwError.tempUnavailable", Boolean.TRUE);
            String tmdbId = getMovieId(videoData, throwTempError);

            if (!StringUtils.isNumeric(tmdbId)) {
                LOG.debug("TMDb id not available '{}'", videoData.getTitle());
                return ScanResult.MISSING_ID;
            }

            movieDb = tmdbApiWrapper.getMovieInfoByTMDB(Integer.parseInt(tmdbId), throwTempError);
            movieCasts = tmdbApiWrapper.getMovieCredits(movieDb.getId(), throwTempError);
        } catch (TemporaryUnavailableException ex) {
            // check retry
            int maxRetries = configServiceWrapper.getIntProperty("themoviedb.maxRetries.movie", 0);
            if (videoData.getRetries() < maxRetries) {
                return ScanResult.RETRY;
            }
        }

        if (movieDb == null || movieCasts == null) {
            LOG.error("Can't find informations for movie '{}'", videoData.getTitle());
            return ScanResult.ERROR;
        }

        // fill in data
        videoData.setSourceDbId(ImdbScanner.SCANNER_ID, StringUtils.trim(movieDb.getImdbID()));

        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.checkOverwriteTagline(videoData, SCANNER_ID)) {
            videoData.setOutline(StringUtils.trim(movieDb.getTagline()), SCANNER_ID);
        }

        if (OverrideTools.checkOverwriteCountries(videoData, SCANNER_ID)) {
            Set<String> countryNames = new LinkedHashSet<>();
            for (ProductionCountry country : movieDb.getProductionCountries()) {
                countryNames.add(country.getName());
            }
            videoData.setCountryNames(countryNames, SCANNER_ID);
        }

        String releaseDateString = movieDb.getReleaseDate();
        if (StringUtils.isNotBlank(releaseDateString) && !"1900-01-01".equals(releaseDateString)) {
            Date releaseDate = MetadataTools.parseToDate(releaseDateString);
            if (releaseDate != null) {
                if (OverrideTools.checkOverwriteReleaseDate(videoData, SCANNER_ID)) {
                    videoData.setReleaseDate(releaseDate, SCANNER_ID);
                }
                if (OverrideTools.checkOverwriteYear(videoData, SCANNER_ID)) {
                    videoData.setPublicationYear(MetadataTools.extractYearAsInt(releaseDate), SCANNER_ID);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(movieDb.getGenres())) {
            if (OverrideTools.checkOverwriteGenres(videoData, SCANNER_ID)) {
                Set<String> genreNames = new HashSet<>();
                for (com.omertron.themoviedbapi.model.Genre genre : movieDb.getGenres()) {
                    if (StringUtils.isNotBlank(genre.getName())) {
                        genreNames.add(StringUtils.trim(genre.getName()));
                    }
                }
                videoData.setGenreNames(genreNames, SCANNER_ID);
            }
        }

        if (CollectionUtils.isNotEmpty(movieDb.getProductionCompanies())) {
            if (OverrideTools.checkOverwriteStudios(videoData, SCANNER_ID)) {
                Set<String> studioNames = new HashSet<>();
                for (ProductionCompany company : movieDb.getProductionCompanies()) {
                    if (StringUtils.isNotBlank(company.getName())) {
                        studioNames.add(StringUtils.trim(company.getName()));
                    }
                }
                videoData.setStudioNames(studioNames, SCANNER_ID);
            }
        }

        // CAST
        if (!this.configServiceWrapper.isCastScanEnabled(JobType.ACTOR)) {
            for (MediaCreditCast person : movieCasts.getCast()) {
                CreditDTO credit = new CreditDTO(SCANNER_ID, String.valueOf(person.getId()), JobType.ACTOR,
                        person.getName(), person.getCharacter());
                videoData.addCreditDTO(credit);
            }
        }

        // GUEST STARS
        if (!this.configServiceWrapper.isCastScanEnabled(JobType.GUEST_STAR)) {
            for (MediaCreditCast person : movieCasts.getGuestStars()) {
                CreditDTO credit = new CreditDTO(SCANNER_ID, String.valueOf(person.getId()), JobType.GUEST_STAR,
                        person.getName(), person.getCharacter());
                videoData.addCreditDTO(credit);
            }
        }

        // CREW
        for (MediaCreditCrew person : movieCasts.getCrew()) {
            JobType jobType = retrieveJobType(person.getDepartment());
            if (!this.configServiceWrapper.isCastScanEnabled(jobType)) {
                // scan not enabled for that job
                continue;
            }

            CreditDTO credit = new CreditDTO(SCANNER_ID, String.valueOf(person.getId()), jobType, person.getName(),
                    person.getJob());
            videoData.addCreditDTO(credit);
        }

        return ScanResult.OK;
    }

    @Override
    public String getPersonId(Person person) {
        return getPersonId(person, false);
    }

    private String getPersonId(Person person, boolean throwTempError) {
        String tmdbId = person.getSourceDbId(SCANNER_ID);
        if (StringUtils.isNumeric(tmdbId)) {
            return tmdbId;
        }

        if (StringUtils.isNotBlank(person.getName())) {
            tmdbId = tmdbApiWrapper.getPersonId(person.getName(), throwTempError);
            person.setSourceDbId(SCANNER_ID, tmdbId);
        }

        return tmdbId;
    }

    @Override
    public ScanResult scan(Person person) {
        PersonInfo tmdbPerson = null;
        try {
            boolean throwTempError = configServiceWrapper
                    .getBooleanProperty("themoviedb.throwError.tempUnavailable", Boolean.TRUE);
            String tmdbId = getPersonId(person, throwTempError);

            if (!StringUtils.isNumeric(tmdbId)) {
                LOG.debug("TMDb id not available '{}'", person.getName());
                return ScanResult.MISSING_ID;
            }

            tmdbPerson = tmdbApiWrapper.getPersonInfo(Integer.parseInt(tmdbId), throwTempError);
        } catch (TemporaryUnavailableException ex) {
            // check retry
            int maxRetries = configServiceWrapper.getIntProperty("themoviedb.maxRetries.person", 0);
            if (person.getRetries() < maxRetries) {
                return ScanResult.RETRY;
            }
        }

        if (tmdbPerson == null) {
            LOG.error("Can't find information for person '{}'", person.getName());
            return ScanResult.ERROR;
        }

        // fill in data
        person.setSourceDbId(ImdbScanner.SCANNER_ID, StringUtils.trim(tmdbPerson.getImdbId()));

        if (OverrideTools.checkOverwritePersonNames(person, SCANNER_ID)) {
            // split person names
            PersonNameDTO nameDTO = MetadataTools.splitFullName(tmdbPerson.getName());
            if (OverrideTools.checkOverwriteName(person, SCANNER_ID)) {
                person.setName(nameDTO.getName(), SCANNER_ID);
            }
            if (OverrideTools.checkOverwriteName(person, SCANNER_ID)) {
                person.setFirstName(nameDTO.getFirstName(), SCANNER_ID);
            }
            if (OverrideTools.checkOverwriteLastName(person, SCANNER_ID)) {
                person.setLastName(nameDTO.getLastName(), SCANNER_ID);
            }
        }

        if (OverrideTools.checkOverwriteBirthDay(person, SCANNER_ID)) {
            Date parsedDate = MetadataTools.parseToDate(tmdbPerson.getBirthday());
            person.setBirthDay(parsedDate, SCANNER_ID);
        }

        if (OverrideTools.checkOverwriteBirthPlace(person, SCANNER_ID)) {
            person.setBirthPlace(StringUtils.trimToNull(tmdbPerson.getPlaceOfBirth()), SCANNER_ID);
        }

        if (OverrideTools.checkOverwriteBirthName(person, SCANNER_ID)) {
            if (CollectionUtils.isNotEmpty(tmdbPerson.getAlsoKnownAs())) {
                String birthName = tmdbPerson.getAlsoKnownAs().get(0);
                person.setBirthName(birthName, SCANNER_ID);
            }
        }

        if (OverrideTools.checkOverwriteDeathDay(person, SCANNER_ID)) {
            Date parsedDate = MetadataTools.parseToDate(tmdbPerson.getDeathday());
            person.setDeathDay(parsedDate, SCANNER_ID);
        }

        if (OverrideTools.checkOverwriteBiography(person, SCANNER_ID)) {
            person.setBiography(cleanBiography(tmdbPerson.getBiography()), SCANNER_ID);
        }

        return ScanResult.OK;
    }

    /**
     * Remove unneeded text from the biography
     *
     * @param bio
     * @return
     */
    private static 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();
    }

    @Override
    public boolean isFilmographyScanEnabled() {
        return configServiceWrapper.getBooleanProperty("themoviedb.person.filmography", false);
    }

    @Override
    public ScanResult scanFilmography(Person person) {
        PersonCreditList<CreditBasic> credits = null;
        try {
            boolean throwTempError = configServiceWrapper
                    .getBooleanProperty("themoviedb.throwError.tempUnavailable", Boolean.TRUE);
            String tmdbId = getPersonId(person, throwTempError);

            if (!StringUtils.isNumeric(tmdbId)) {
                LOG.debug("TMDb id not available '{}'", person.getName());
                return ScanResult.MISSING_ID;
            }

            credits = tmdbApiWrapper.getPersonCredits(Integer.parseInt(tmdbId), throwTempError);
        } catch (TemporaryUnavailableException ex) {
            // check retry
            int maxRetries = configServiceWrapper.getIntProperty("themoviedb.maxRetries.filmography", 0);
            if (person.getRetries() < maxRetries) {
                return ScanResult.RETRY;
            }
        }

        if (credits == null) {
            LOG.error("Can't find filmography for person '{}'", person.getName());
            return ScanResult.ERROR;
        }

        // fill in data
        Set<FilmParticipation> newFilmography = new HashSet<>();
        for (CreditBasic credit : credits.getCast()) {
            JobType jobType = retrieveJobType(credit.getDepartment());
            if (jobType == null) {
                // job type must be present
                continue;
            }

            FilmParticipation filmo = null;
            switch (credit.getMediaType()) {
            case MOVIE:
                filmo = convertMovieCreditToFilm((CreditMovieBasic) credit, person, jobType);
                break;
            case TV:
                LOG.debug("TV credit information for {} ({}) not used: {}", person.getName(), jobType,
                        credit.toString());
                break;
            case EPISODE:
                LOG.debug("TV Episode credit information for {} ({}) not used: {}", person.getName(), jobType,
                        credit.toString());
                break;
            default:
                LOG.debug("Unknown media type '{}' for credit {}", credit.getMediaType(), credit.toString());
            }

            if (filmo != null) {
                newFilmography.add(filmo);
            }
        }
        person.setNewFilmography(newFilmography);

        return ScanResult.OK;
    }

    private FilmParticipation convertMovieCreditToFilm(CreditMovieBasic credit, Person person, JobType jobType) {
        Date releaseDate = MetadataTools.parseToDate(credit.getReleaseDate());
        if (releaseDate == null) {
            // release date must be present
            return null;
        }

        FilmParticipation filmo = new FilmParticipation();
        filmo.setParticipationType(ParticipationType.MOVIE);
        filmo.setSourceDb(SCANNER_ID);
        filmo.setSourceDbId(String.valueOf(credit.getId()));
        filmo.setPerson(person);
        filmo.setJobType(jobType);
        if (JobType.ACTOR == jobType) {
            filmo.setRole(credit.getCharacter());
        }
        filmo.setTitle(credit.getTitle());
        filmo.setTitleOriginal(StringUtils.trimToNull(credit.getOriginalTitle()));
        filmo.setReleaseDate(releaseDate);
        filmo.setYear(MetadataTools.extractYearAsInt(releaseDate));
        return filmo;
    }

    private static JobType retrieveJobType(String department) {
        switch (department) {
        case "writing":
            return JobType.WRITER;
        case "directing":
            return JobType.DIRECTOR;
        case "production":
            return JobType.PRODUCER;
        case "sound":
            return JobType.SOUND;
        case "camera":
            return JobType.CAMERA;
        case "art":
            return JobType.ART;
        case "editing":
            return JobType.EDITING;
        case "costume & make-up":
            return JobType.COSTUME_MAKEUP;
        case "crew":
            return JobType.CREW;
        case "visual effects":
            return JobType.EFFECTS;
        case "lighting":
            return JobType.LIGHTING;
        default:
            LOG.debug("Unknown department '{}'", department);
            return JobType.UNKNOWN;
        }
    }

    @Override
    public boolean scanNFO(String nfoContent, InfoDTO dto, boolean ignorePresentId) {
        // if we already have the ID, skip the scanning of the NFO file
        if (!ignorePresentId && StringUtils.isNotBlank(dto.getId(SCANNER_ID))) {
            return Boolean.TRUE;
        }

        LOG.trace("Scanning NFO for TheMovieDb ID");

        try {
            int beginIndex = nfoContent.indexOf("/movie/");
            if (beginIndex != -1) {
                StringTokenizer st = new StringTokenizer(nfoContent.substring(beginIndex + 7),
                        "/ \n,:!&\"'(--_)=$");
                String sourceId = st.nextToken();
                LOG.debug("Allocine ID found in NFO: {}", sourceId);
                dto.addId(SCANNER_ID, sourceId);
                return Boolean.TRUE;
            }
        } catch (Exception ex) {
            LOG.trace("NFO scanning error", ex);
        }

        LOG.debug("No TheMovieDb ID found in NFO");
        return Boolean.FALSE;
    }
}