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.scanner.artwork; import com.moviejukebox.model.IImage; import com.moviejukebox.model.Jukebox; import com.moviejukebox.model.Movie; import com.moviejukebox.model.enumerations.DirtyFlag; import com.moviejukebox.plugin.*; import com.moviejukebox.scanner.AttachmentScanner; import com.moviejukebox.tools.*; import com.omertron.themoviedbapi.MovieDbException; import com.omertron.themoviedbapi.TheMovieDbApi; import com.omertron.themoviedbapi.enumeration.SearchType; import com.omertron.themoviedbapi.model.movie.MovieInfo; import com.omertron.themoviedbapi.results.ResultList; import com.omertron.thetvdbapi.model.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLDecoder; import java.util.*; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Scanner for fanart files in local directory * * @author Stuart.Boston * @version 1.0, 10th December 2008 - Initial code * @version 1.1, 19th July 2009 - Added Internet search */ public final class FanartScanner { private static final Logger LOG = LoggerFactory.getLogger(FanartScanner.class); private static final Collection<String> FANART_SCANNER_EXT = Collections .synchronizedList(new ArrayList<String>()); private static final String FANART_TOKEN; private static final String FANART_EXT = PropertiesUtil.getProperty("fanart.format", "jpg"); private static final boolean FANART_OVERWRITE; private static final boolean USE_FOLDER_IMAGE = PropertiesUtil .getBooleanProperty("fanart.scanner.useFolderImage", Boolean.FALSE); private static final Collection<String> IMAGE_NAMES = Collections.synchronizedList(new ArrayList<String>()); private static final boolean ARTWORK_VALIDATE; private static final int ARTWORK_VALIDATE_MATCH; private static TheMovieDbApi TMDB = null; static { // We get valid extensions synchronized (FANART_SCANNER_EXT) { if (FANART_SCANNER_EXT.isEmpty()) { StringTokenizer st = new StringTokenizer( PropertiesUtil.getProperty("fanart.scanner.fanartExtensions", "jpg,jpeg,gif,bmp,png"), ",;| "); while (st.hasMoreTokens()) { FANART_SCANNER_EXT.add(st.nextToken()); } } } FANART_TOKEN = PropertiesUtil.getProperty("mjb.scanner.fanartToken", ".fanart"); FANART_OVERWRITE = PropertiesUtil.getBooleanProperty("mjb.forceFanartOverwrite", Boolean.FALSE); // See if we use background.* or fanart.* synchronized (IMAGE_NAMES) { if (USE_FOLDER_IMAGE && IMAGE_NAMES.isEmpty()) { StringTokenizer st = new StringTokenizer( PropertiesUtil.getProperty("fanart.scanner.imageName", "fanart,backdrop,background"), ",;|"); while (st.hasMoreTokens()) { IMAGE_NAMES.add(st.nextToken()); } } } ARTWORK_VALIDATE = PropertiesUtil.getBooleanProperty("fanart.scanner.Validate", Boolean.TRUE); ARTWORK_VALIDATE_MATCH = PropertiesUtil.getIntProperty("fanart.scanner.ValidateMatch", 75); try { TMDB = new TheMovieDbApi(PropertiesUtil.getProperty("API_KEY_TheMovieDB")); } catch (MovieDbException ex) { LOG.warn("Failed to initialise TheMovieDB API. Fanart will not be downloaded."); LOG.warn(SystemTools.getStackTrace(ex)); } } private FanartScanner() { throw new UnsupportedOperationException("Class cannot be instantiated"); } /** * Get the Fanart URL for the movie from the source sites * * @param movie * @return */ public static String getFanartURL(Movie movie) { return movie.isTVShow() ? getTvFanartURL(movie) : getMovieFanartURL(movie); } public static boolean scan(MovieImagePlugin backgroundPlugin, Jukebox jukebox, Movie movie) { String localFanartBaseFilename = movie.getBaseFilename(); String parentPath = FileTools.getParentFolder(movie.getFile()); // Look for the videoname.fanartToken.Extension String fullFanartFilename = StringTools.appendToPath(parentPath, localFanartBaseFilename + FANART_TOKEN); File localFanartFile = FileTools.findFileFromExtensions(fullFanartFilename, FANART_SCANNER_EXT); boolean foundLocalFanart = localFanartFile.exists(); // Try searching the fileCache for the filename. if (!foundLocalFanart) { Boolean searchInJukebox = Boolean.TRUE; // if the fanart URL is invalid, but the fanart filename is valid, then this is likely a recheck, so don't search on the jukebox folder if (StringTools.isNotValidString(movie.getFanartURL()) && StringTools.isValidString(movie.getFanartFilename())) { searchInJukebox = Boolean.FALSE; } localFanartFile = FileTools.findFilenameInCache(localFanartBaseFilename + FANART_TOKEN, FANART_SCANNER_EXT, jukebox, searchInJukebox); if (localFanartFile != null) { foundLocalFanart = true; } } // if no fanart has been found, try the foldername.fanartToken.Extension if (!foundLocalFanart) { localFanartBaseFilename = FileTools.getParentFolderName(movie.getFile()); // Checking for the MovieFolderName.* fullFanartFilename = StringTools.appendToPath(parentPath, localFanartBaseFilename + FANART_TOKEN); localFanartFile = FileTools.findFileFromExtensions(fullFanartFilename, FANART_SCANNER_EXT); foundLocalFanart = localFanartFile.exists(); } // Check for fanart.* and background.* fanart. if (!foundLocalFanart && USE_FOLDER_IMAGE) { // Check for each of the farnartImageName.* files for (String fanartFilename : IMAGE_NAMES) { fullFanartFilename = StringTools.appendToPath(parentPath, fanartFilename); localFanartFile = FileTools.findFileFromExtensions(fullFanartFilename, FANART_SCANNER_EXT); foundLocalFanart = localFanartFile.exists(); if (!foundLocalFanart && movie.isTVShow()) { // Get the parent directory and check that fullFanartFilename = StringTools.appendToPath( FileTools.getParentFolder(movie.getFile().getParentFile().getParentFile()), fanartFilename); localFanartFile = FileTools.findFileFromExtensions(fullFanartFilename, FANART_SCANNER_EXT); foundLocalFanart = localFanartFile.exists(); if (foundLocalFanart) { break; // We found the artwork so quit the loop } } else { break; // We found the artwork so quit the loop } } } // Check file attachments if (!foundLocalFanart) { localFanartFile = AttachmentScanner.extractAttachedFanart(movie); foundLocalFanart = (localFanartFile != null); } // If we've found the fanart, copy it to the jukebox, otherwise download it. if (foundLocalFanart && (localFanartFile != null)) { fullFanartFilename = localFanartFile.getAbsolutePath(); LOG.debug("File {} found", fullFanartFilename); if (StringTools.isNotValidString(movie.getFanartFilename())) { movie.setFanartFilename(movie.getBaseFilename() + FANART_TOKEN + "." + FileTools.getFileExtension(localFanartFile.getName())); } if (StringTools.isNotValidString(movie.getFanartURL())) { movie.setFanartURL(localFanartFile.toURI().toString()); } String fanartFilename = movie.getFanartFilename(); String finalDestinationFileName = StringTools.appendToPath(jukebox.getJukeboxRootLocationDetails(), fanartFilename); String destFileName = StringTools.appendToPath(jukebox.getJukeboxTempLocationDetails(), fanartFilename); File finalDestinationFile = FileTools.fileCache.getFile(finalDestinationFileName); File fullFanartFile = new File(fullFanartFilename); // Local Fanart is newer OR ForceFanartOverwrite OR DirtyFanart // Can't check the file size because the jukebox fanart may have been re-sized // This may mean that the local art is different to the jukebox art even if the local file date is newer if (FileTools.isNewer(fullFanartFile, finalDestinationFile) || FANART_OVERWRITE || movie.isDirty(DirtyFlag.FANART)) { try { BufferedImage fanartImage = GraphicTools.loadJPEGImage(fullFanartFile); if (fanartImage != null) { fanartImage = backgroundPlugin.generate(movie, fanartImage, "fanart", null); if (PropertiesUtil.getBooleanProperty("fanart.perspective", Boolean.FALSE)) { destFileName = destFileName.subSequence(0, destFileName.lastIndexOf('.') + 1) + "png"; movie.setFanartFilename(destFileName); } GraphicTools.saveImageToDisk(fanartImage, destFileName); LOG.debug("{} has been copied to {}", fullFanartFilename, destFileName); } else { movie.setFanartFilename(Movie.UNKNOWN); movie.setFanartURL(Movie.UNKNOWN); } } catch (IOException ex) { LOG.debug("Failed loading fanart: {} - {}", fullFanartFilename, ex.getMessage()); } } else { LOG.debug("{} already exists", finalDestinationFileName); } } else { LOG.debug("No local Fanart found for {} attempting to download", movie.getBaseFilename()); checkFanartUrl(movie); downloadFanart(backgroundPlugin, jukebox, movie); } return foundLocalFanart; } public static String generateFanartFilename(Movie movie) { StringBuilder filename; filename = new StringBuilder(FileTools.makeSafeFilename(movie.getBaseName())).append(FANART_TOKEN) .append(".").append(FANART_EXT); return filename.toString(); } private static void checkFanartUrl(Movie movie) { boolean ignore = movie.skipOnlineScan(); if (!ignore && StringTools.isNotValidString(movie.getFanartURL())) { LOG.debug("{}: No fanart URL found, attempting to scan online", movie.getBaseName()); String url = getFanartURL(movie); if (StringTools.isValidString(url)) { movie.setFanartURL(url); movie.setFanartFilename(generateFanartFilename(movie)); } else { LOG.debug("{}: No fanart URL found.", movie.getBaseName()); } } } private static void downloadFanart(MovieImagePlugin backgroundPlugin, Jukebox jukebox, Movie movie) { if (StringTools.isValidString(movie.getFanartURL())) { String safeFanartFilename = movie.getFanartFilename(); String fanartFilename = StringTools.appendToPath(jukebox.getJukeboxRootLocationDetails(), safeFanartFilename); File fanartFile = FileTools.fileCache.getFile(fanartFilename); String tmpDestFileName = StringTools.appendToPath(jukebox.getJukeboxTempLocationDetails(), safeFanartFilename); File tmpDestFile = new File(tmpDestFileName); // Do not overwrite existing fanart unless ForceFanartOverwrite = true if (FANART_OVERWRITE || (!fanartFile.exists() && !tmpDestFile.exists()) || movie.isDirty(DirtyFlag.FANART)) { FileTools.makeDirsForFile(tmpDestFile); try { LOG.debug("Downloading fanart for {} to {} [calling plugin]", movie.getBaseFilename(), tmpDestFileName); FileTools.downloadImage(tmpDestFile, URLDecoder.decode(movie.getFanartURL(), "UTF-8")); BufferedImage fanartImage = GraphicTools.loadJPEGImage(tmpDestFile); if (fanartImage != null) { fanartImage = backgroundPlugin.generate(movie, fanartImage, null, null); GraphicTools.saveImageToDisk(fanartImage, tmpDestFileName); } else { movie.setFanartFilename(Movie.UNKNOWN); movie.setFanartURL(Movie.UNKNOWN); } } catch (IOException error) { LOG.debug("Failed to download fanart: {} removing from movie details - {}", movie.getFanartURL(), error.getMessage()); movie.setFanartFilename(Movie.UNKNOWN); movie.setFanartURL(Movie.UNKNOWN); } } else { LOG.debug("Fanart exists for {}", movie.getBaseFilename()); } } } /** * Get the Fanart for the movie from TheMovieDB.org * * @author Stuart.Boston * @param movie The movie to get the fanart for * @return A string URL pointing to the fanart */ private static String getMovieFanartURL(Movie movie) { // Unable to scan for fanart because TheMovieDB wasn't initialised if (TMDB == null) { return Movie.UNKNOWN; } String language = PropertiesUtil.getProperty("themoviedb.language", "en-US"); MovieInfo moviedb = null; String imdbID = movie.getId(ImdbPlugin.IMDB_PLUGIN_ID); String tmdbIDstring = movie.getId(TheMovieDbPlugin.TMDB_PLUGIN_ID); int tmdbID; if (StringUtils.isNumeric(tmdbIDstring)) { tmdbID = Integer.parseInt(tmdbIDstring); } else { tmdbID = 0; } if (tmdbID > 0) { try { moviedb = TMDB.getMovieInfo(tmdbID, language); } catch (MovieDbException ex) { LOG.debug("Failed to get fanart using TMDB ID: {} - {}", tmdbID, ex.getMessage()); } } if (moviedb == null && StringTools.isValidString(imdbID)) { try { // The ImdbLookup contains images moviedb = TMDB.getMovieInfoImdb(imdbID, language); } catch (MovieDbException ex) { LOG.debug("Failed to get fanart using IMDB ID: {} - {}", imdbID, ex.getMessage()); } } if (moviedb == null) { try { ResultList<MovieInfo> result = TMDB.searchMovie(movie.getOriginalTitle(), 0, language, TheMovieDbPlugin.INCLUDE_ADULT, 0, 0, SearchType.PHRASE); List<MovieInfo> movieList = result.getResults(); for (MovieInfo m : movieList) { if (m.getTitle().equals(movie.getTitle()) || m.getTitle().equalsIgnoreCase(movie.getOriginalTitle()) || m.getOriginalTitle().equalsIgnoreCase(movie.getTitle()) || m.getOriginalTitle().equalsIgnoreCase(movie.getOriginalTitle())) { if (StringTools.isNotValidString(movie.getYear())) { // We don't have a year for the movie, so assume this is the correct movie moviedb = m; break; } else if (m.getReleaseDate().contains(movie.getYear())) { // found the movie name and year moviedb = m; break; } } } } catch (MovieDbException ex) { LOG.debug("Failed to get fanart using IMDB ID: {} - {}", imdbID, ex.getMessage()); } } // Check that the returned movie isn't null if (moviedb == null) { LOG.debug("Error getting fanart from TheMovieDB.org for {}", movie.getBaseFilename()); return Movie.UNKNOWN; } try { URL fanart = TMDB.createImageUrl(moviedb.getBackdropPath(), "original"); if (fanart == null) { return Movie.UNKNOWN; } return fanart.toString(); } catch (MovieDbException ex) { LOG.debug("Error getting fanart from TheMovieDB.org for {}", movie.getBaseFilename()); return Movie.UNKNOWN; } } /** * Get the Fanart for the movie from TheTVDb.com * * @author Stuart.Boston * @param movie The movie bean to get the fanart for * @return A string URL pointing to the fanart */ private static String getTvFanartURL(Movie movie) { String url = null; String id = TheTvDBPlugin.findId(movie); if (StringTools.isNotValidString(id)) { return Movie.UNKNOWN; } Series series = TheTvDBPlugin.getSeries(id); if (series == null) { return Movie.UNKNOWN; } Banners banners = TheTvDBPlugin.getBanners(id); if (!banners.getFanartList().isEmpty()) { // Pick a fanart that is not likely to be the same as a previous one. int index = movie.getSeason(); if (index < 0) { index = 0; } // Make sure that the index is not more than the list of available banners // We may still run into issues, if there are less HD than this number index = (index % banners.getFanartList().size()); Banner bannerSD = null; Banner bannerHD = null; int countSD = 0; int countHD = 0; for (Banner banner : banners.getFanartList()) { if (banner.getBannerType2() == BannerType.FANART_HD) { bannerHD = banner; // Save the current banner countHD++; if (countHD >= index) { // We have a HD banner, so quit break; } } else // This is a SD banner, So save it in case we can't find a HD one { if (countSD <= index) { // Only update the banner if we want a later one bannerSD = banner; } } } if (bannerHD != null) { url = bannerHD.getUrl(); } else if (bannerSD != null) { url = bannerSD.getUrl(); } } if (StringTools.isNotValidString(url) && StringTools.isValidString(series.getFanart())) { url = series.getFanart(); } return url; } public static boolean validateArtwork(IImage artworkImage, int artworkWidth, int artworkHeight, boolean checkAspect) { @SuppressWarnings("rawtypes") Iterator readers = ImageIO.getImageReadersBySuffix("jpeg"); ImageReader reader = (ImageReader) readers.next(); int urlWidth, urlHeight; float urlAspect; if (!ARTWORK_VALIDATE) { return true; } if (StringTools.isNotValidString(artworkImage.getUrl())) { return false; } try { URL url = new URL(artworkImage.getUrl()); try (InputStream in = url.openStream(); ImageInputStream iis = ImageIO.createImageInputStream(in)) { reader.setInput(iis, true); urlWidth = reader.getWidth(0); urlHeight = reader.getHeight(0); } } catch (IOException error) { LOG.debug("ValidateFanart error: {}: can't open url", error.getMessage()); return false; // Quit and return a false fanart } urlAspect = (float) urlWidth / (float) urlHeight; if (checkAspect && urlAspect < 1.0) { LOG.debug("ValidateFanart {} rejected: URL is portrait format", artworkImage); return false; } // Adjust fanart width / height by the ValidateMatch figure int newArtworkWidth = artworkWidth * (ARTWORK_VALIDATE_MATCH / 100); int newArtworkHeight = artworkHeight * (ARTWORK_VALIDATE_MATCH / 100); if (urlWidth < newArtworkWidth) { LOG.debug("{} rejected: URL width ({}) is smaller than fanart width ({})", artworkImage, urlWidth, newArtworkWidth); return false; } if (urlHeight < newArtworkHeight) { LOG.debug("{} rejected: URL height ({}) is smaller than fanart height ({})", artworkImage, urlHeight, newArtworkHeight); return false; } return true; } }