org.tinymediamanager.core.movie.tasks.MovieScrapeTask.java Source code

Java tutorial

Introduction

Here is the source code for org.tinymediamanager.core.movie.tasks.MovieScrapeTask.java

Source

/*
 * Copyright 2012 - 2016 Manuel Laggner
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tinymediamanager.core.movie.tasks;

import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.ResourceBundle;

import javax.swing.SwingUtilities;

import org.apache.commons.lang3.LocaleUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinymediamanager.core.MediaFileType;
import org.tinymediamanager.core.Message;
import org.tinymediamanager.core.Message.MessageLevel;
import org.tinymediamanager.core.MessageManager;
import org.tinymediamanager.core.entities.MediaFile;
import org.tinymediamanager.core.movie.MovieList;
import org.tinymediamanager.core.movie.MovieModuleManager;
import org.tinymediamanager.core.movie.MovieScraperMetadataConfig;
import org.tinymediamanager.core.movie.MovieSearchAndScrapeOptions;
import org.tinymediamanager.core.movie.entities.Movie;
import org.tinymediamanager.core.movie.entities.MovieTrailer;
import org.tinymediamanager.core.threading.TmmTask;
import org.tinymediamanager.core.threading.TmmTaskManager;
import org.tinymediamanager.core.threading.TmmThreadPool;
import org.tinymediamanager.scraper.MediaMetadata;
import org.tinymediamanager.scraper.MediaScrapeOptions;
import org.tinymediamanager.scraper.MediaScraper;
import org.tinymediamanager.scraper.MediaSearchResult;
import org.tinymediamanager.scraper.entities.MediaArtwork;
import org.tinymediamanager.scraper.entities.MediaArtwork.MediaArtworkType;
import org.tinymediamanager.scraper.entities.MediaTrailer;
import org.tinymediamanager.scraper.entities.MediaType;
import org.tinymediamanager.scraper.mediaprovider.IMovieArtworkProvider;
import org.tinymediamanager.scraper.mediaprovider.IMovieMetadataProvider;
import org.tinymediamanager.scraper.mediaprovider.IMovieTrailerProvider;
import org.tinymediamanager.scraper.trakttv.SyncTraktTvTask;
import org.tinymediamanager.ui.UTF8Control;
import org.tinymediamanager.ui.movies.dialogs.MovieChooserDialog;

/**
 * The Class MovieScrapeTask.
 * 
 * @author Manuel Laggner
 */
public class MovieScrapeTask extends TmmThreadPool {
    private final static Logger LOGGER = LoggerFactory.getLogger(MovieScrapeTask.class);
    private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("messages", new UTF8Control()); //$NON-NLS-1$

    private List<Movie> moviesToScrape;
    private boolean doSearch;
    private MovieSearchAndScrapeOptions options;
    private List<Movie> smartScrapeList;

    public MovieScrapeTask(List<Movie> moviesToScrape, boolean doSearch, MovieSearchAndScrapeOptions options) {
        super(BUNDLE.getString("movie.scraping"));
        this.moviesToScrape = moviesToScrape;
        this.doSearch = doSearch;
        this.options = options;
    }

    @Override
    protected void doInBackground() {
        initThreadPool(3, "scrape");
        start();

        smartScrapeList = new ArrayList<>(0);

        for (int i = 0; i < moviesToScrape.size(); i++) {
            Movie movie = moviesToScrape.get(i);
            submitTask(new Worker(movie));
        }
        waitForCompletionOrCancel();

        // initiate smart scrape
        if (!smartScrapeList.isEmpty() && !GraphicsEnvironment.isHeadless()) {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    @Override
                    public void run() {
                        for (Movie movie : smartScrapeList) {
                            MovieChooserDialog dialogMovieChooser = new MovieChooserDialog(movie,
                                    smartScrapeList.size() > 1 ? true : false);
                            if (!dialogMovieChooser.showDialog()) {
                                break;
                            }
                        }
                    }
                });
            } catch (Exception e) {
                LOGGER.error("SmartScrape crashed " + e.getMessage());
            }
        }

        if (MovieModuleManager.MOVIE_SETTINGS.getSyncTrakt()) {
            TmmTask task = new SyncTraktTvTask(moviesToScrape, null);
            TmmTaskManager.getInstance().addUnnamedTask(task);
        }

        LOGGER.info("Done scraping movies)");
    }

    @Override
    public void callback(Object obj) {
        // do not publish task description here, because with different workers the text is never right
        publishState(progressDone);
    }

    /****************************************************************************************
     * Helper classes
     ****************************************************************************************/
    private class Worker implements Runnable {
        private MovieList movieList;
        private Movie movie;

        public Worker(Movie movie) {
            this.movie = movie;
        }

        @Override
        public void run() {
            try {
                movieList = MovieList.getInstance();
                // set up scrapers
                MovieScraperMetadataConfig scraperMetadataConfig = options.getScraperMetadataConfig();
                MediaScraper mediaMetadataScraper = options.getMetadataScraper();
                List<MediaScraper> artworkScrapers = options.getArtworkScrapers();
                List<MediaScraper> trailerScrapers = options.getTrailerScrapers();

                // search movie
                MediaSearchResult result1 = null;
                if (doSearch) {
                    result1 = searchForMovie(mediaMetadataScraper);
                    if (result1 == null) {
                        // append this search request to the UI with search & scrape dialog
                        synchronized (smartScrapeList) {
                            smartScrapeList.add(movie);
                            return;
                        }
                    }
                }

                // get metadata, artwork and trailers
                if ((doSearch && result1 != null) || !doSearch) {
                    try {
                        MediaScrapeOptions options = new MediaScrapeOptions(MediaType.MOVIE);
                        options.setResult(result1);
                        options.setLanguage(LocaleUtils
                                .toLocale(MovieModuleManager.MOVIE_SETTINGS.getScraperLanguage().name()));
                        options.setCountry(MovieModuleManager.MOVIE_SETTINGS.getCertificationCountry());
                        options.setFanartSize(MovieModuleManager.MOVIE_SETTINGS.getImageFanartSize());
                        options.setPosterSize(MovieModuleManager.MOVIE_SETTINGS.getImagePosterSize());

                        // we didn't do a search - pass imdbid and tmdbid from movie object
                        if (!doSearch) {
                            for (Entry<String, Object> entry : movie.getIds().entrySet()) {
                                options.setId(entry.getKey(), entry.getValue().toString());
                            }
                        } else {
                            // override scraper with one from search result
                            mediaMetadataScraper = movieList.getMediaScraperById(result1.getProviderId());
                        }

                        // scrape metadata if wanted
                        MediaMetadata md = null;

                        if (mediaMetadataScraper != null && mediaMetadataScraper.getMediaProvider() != null) {
                            LOGGER.info("=====================================================");
                            LOGGER.info("Scraper metadata with scraper: "
                                    + mediaMetadataScraper.getMediaProvider().getProviderInfo().getId() + ", "
                                    + mediaMetadataScraper.getMediaProvider().getProviderInfo().getVersion());
                            LOGGER.info(options.toString());
                            LOGGER.info("=====================================================");
                            md = ((IMovieMetadataProvider) mediaMetadataScraper.getMediaProvider())
                                    .getMetadata(options);

                            if (scraperMetadataConfig.isMetadata()) {
                                movie.setMetadata(md, scraperMetadataConfig);
                            }

                            // scrape artwork if wanted
                            if (scraperMetadataConfig.isArtwork()) {
                                movie.setArtwork(getArtwork(movie, md, artworkScrapers), scraperMetadataConfig);
                            }

                            // scrape trailer if wanted
                            if (scraperMetadataConfig.isTrailer()) {
                                movie.setTrailers(getTrailers(movie, md, trailerScrapers));
                            }
                        }
                    } catch (Exception e) {
                        LOGGER.error("movie.setMetadata", e);
                        MessageManager.instance.pushMessage(
                                new Message(MessageLevel.ERROR, movie, "message.scrape.metadatamoviefailed"));
                    }
                }
            } catch (Exception e) {
                LOGGER.error("Thread crashed", e);
                MessageManager.instance.pushMessage(new Message(MessageLevel.ERROR, "MovieScraper",
                        "message.scrape.threadcrashed", new String[] { ":", e.getLocalizedMessage() }));
            }
        }

        private MediaSearchResult searchForMovie(MediaScraper mediaMetadataProvider) {
            List<MediaSearchResult> results = movieList.searchMovie(movie.getTitle(), movie, mediaMetadataProvider);
            MediaSearchResult result = null;

            if (results != null && !results.isEmpty()) {
                result = results.get(0);
                // check if there is an other result with 100% score
                if (results.size() > 1) {
                    MediaSearchResult result2 = results.get(1);
                    // if both results have 100% score - do not take any result
                    if (result.getScore() == 1 && result2.getScore() == 1) {
                        LOGGER.info("two 100% results, can't decide whitch to take - ignore result");
                        MessageManager.instance
                                .pushMessage(new Message(MessageLevel.ERROR, movie, "movie.scrape.toosimilar"));
                        return null;
                    }
                }

                // get threshold from settings (default 0.75) - to minimize false positives
                final double scraperTreshold = MovieModuleManager.MOVIE_SETTINGS.getScraperThreshold();
                LOGGER.info("using treshold from settings of {}", scraperTreshold);
                if (result.getScore() < scraperTreshold) {
                    LOGGER.info("score is lower than " + scraperTreshold + " (" + result.getScore()
                            + ") - ignore result");
                    MessageManager.instance.pushMessage(new Message(MessageLevel.ERROR, movie,
                            "movie.scrape.toolowscore", new String[] { String.format("%.2f", scraperTreshold) }));
                    return null;
                }
            } else {
                LOGGER.info("no result found for " + movie.getTitle());
                MessageManager.instance
                        .pushMessage(new Message(MessageLevel.ERROR, movie, "movie.scrape.nomatchfound"));
            }

            return result;
        }

        private List<MediaArtwork> getArtwork(Movie movie, MediaMetadata metadata,
                List<MediaScraper> artworkScrapers) {
            List<MediaArtwork> artwork = new ArrayList<>();

            MediaScrapeOptions options = new MediaScrapeOptions(MediaType.MOVIE);
            options.setArtworkType(MediaArtworkType.ALL);
            options.setMetadata(metadata);
            options.setImdbId(movie.getImdbId());
            options.setTmdbId(movie.getTmdbId());
            options.setLanguage(
                    LocaleUtils.toLocale(MovieModuleManager.MOVIE_SETTINGS.getScraperLanguage().name()));
            options.setCountry(MovieModuleManager.MOVIE_SETTINGS.getCertificationCountry());
            options.setFanartSize(MovieModuleManager.MOVIE_SETTINGS.getImageFanartSize());
            options.setPosterSize(MovieModuleManager.MOVIE_SETTINGS.getImagePosterSize());

            // scrape providers till one artwork has been found
            for (MediaScraper scraper : artworkScrapers) {
                IMovieArtworkProvider artworkProvider = (IMovieArtworkProvider) scraper.getMediaProvider();
                try {
                    artwork.addAll(artworkProvider.getArtwork(options));
                } catch (Exception e) {
                    LOGGER.error("getArtwork", e);
                    MessageManager.instance.pushMessage(
                            new Message(MessageLevel.ERROR, movie, "message.scrape.movieartworkfailed"));
                }
            }

            return artwork;
        }

        private List<MovieTrailer> getTrailers(Movie movie, MediaMetadata metadata,
                List<MediaScraper> trailerScrapers) {
            List<MovieTrailer> trailers = new ArrayList<>();

            // add local trailers!
            for (MediaFile mf : movie.getMediaFiles(MediaFileType.TRAILER)) {
                LOGGER.debug("adding local trailer " + mf.getFilename());
                MovieTrailer mt = new MovieTrailer();
                mt.setName(mf.getFilename());
                mt.setProvider("downloaded");
                mt.setQuality(mf.getVideoFormat());
                mt.setInNfo(false);
                mt.setUrl(mf.getFile().toURI().toString());
                trailers.add(mt);
            }

            MediaScrapeOptions options = new MediaScrapeOptions(MediaType.MOVIE);
            options.setMetadata(metadata);
            options.setImdbId(movie.getImdbId());
            options.setTmdbId(movie.getTmdbId());
            options.setLanguage(
                    LocaleUtils.toLocale(MovieModuleManager.MOVIE_SETTINGS.getScraperLanguage().name()));
            options.setCountry(MovieModuleManager.MOVIE_SETTINGS.getCertificationCountry());

            // scrape trailers
            for (MediaScraper trailerScraper : trailerScrapers) {
                try {
                    IMovieTrailerProvider trailerProvider = (IMovieTrailerProvider) trailerScraper
                            .getMediaProvider();
                    List<MediaTrailer> foundTrailers = trailerProvider.getTrailers(options);
                    for (MediaTrailer mediaTrailer : foundTrailers) {
                        MovieTrailer movieTrailer = new MovieTrailer(mediaTrailer);
                        trailers.add(movieTrailer);
                    }
                } catch (Exception e) {
                    LOGGER.error("getTrailers", e);
                    MessageManager.instance.pushMessage(
                            new Message(MessageLevel.ERROR, movie, "message.scrape.movietrailerfailed"));
                }
            }

            return trailers;
        }
    }
}