All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.moviejukebox.plugin.trailer.AppleTrailersPlugin Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/*
 *      Copyright (c) 2004-2012 YAMJ Members
 *      http://code.google.com/p/moviejukebox/people/list
 *
 *      Web: http://code.google.com/p/moviejukebox/
 *
 *      This software is licensed under a Creative Commons License
 *      See this page: http://code.google.com/p/moviejukebox/wiki/License
 *
 *      For any reuse or distribution, you must make clear to others the
 *      license terms of this work.
 */
package com.moviejukebox.plugin.trailer;

import com.moviejukebox.model.ExtraFile;
import com.moviejukebox.model.Movie;
import com.moviejukebox.model.MovieFile;
import com.moviejukebox.tools.PropertiesUtil;
import static com.moviejukebox.tools.PropertiesUtil.FALSE;
import static com.moviejukebox.tools.PropertiesUtil.TRUE;
import com.moviejukebox.tools.StringTools;
import com.moviejukebox.tools.SystemTools;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class AppleTrailersPlugin extends TrailerPlugin {

    private static final Logger logger = Logger.getLogger(AppleTrailersPlugin.class);
    private static String configResolution = PropertiesUtil.getProperty("appletrailers.resolution", "");
    private static boolean configDownload = PropertiesUtil.getBooleanProperty("appletrailers.download", FALSE);
    private static String configTrailerTypes = PropertiesUtil.getProperty("appletrailers.trailertypes", "tlr,clip,tsr,30sec,640w");
    private static int configMax = PropertiesUtil.getIntProperty("appletrailers.max", "0");
    private static boolean configTypesInclude = PropertiesUtil.getBooleanProperty("appletrailers.typesinclude", TRUE);
    private static String configReplaceUrl = PropertiesUtil.getProperty("appletrailers.replaceurl", "www.apple.com");
    private static final String EXTENSIONS = "mov|m4v";
    private static final String SUBDOMAINS = "movies|images|trailers";
    private static final Pattern TRAILER_URL_PATTERN = Pattern.compile("http://(" + SUBDOMAINS + ").apple.com/movies/[^\\\"]+\\.(" + EXTENSIONS + ")");
    private static final String[] RESOLUTION_ARRAY = {"1080p", "720p", "480p", "640", "480"};

    public AppleTrailersPlugin() {
        super();
        trailersPluginName = "AppleTrailers";
        logMessage = "AppleTrailersPlugin: ";
    }

    @Override
    public final boolean generate(Movie movie) {
        // Check if trailer resolution was selected
        if (configResolution.equals("")) {
            return false;
        }

        String movieName = movie.getOriginalTitle();

        String trailerPageUrl = getTrailerPageUrl(movieName);

        movie.setTrailerLastScan(new Date().getTime()); // Set the last scan to now

        if (Movie.UNKNOWN.equalsIgnoreCase(trailerPageUrl)) {
            logger.debug(logMessage + "Trailer not found for " + movie.getBaseName());
            return false;
        }

        LinkedHashSet trailersUrl = new LinkedHashSet();
        LinkedHashSet bestTrailersUrl = new LinkedHashSet();

        getTrailerSubUrl(trailerPageUrl, trailersUrl);

        selectBestTrailer(trailersUrl, bestTrailersUrl);

        int trailerDownloadCnt = 0;

        if (bestTrailersUrl.isEmpty()) {
            logger.debug(logMessage + "No trailers found for " + movie.getBaseName());
            return false;
        }

        boolean isExchangeOk = false;

        for (String trailerRealUrl : bestTrailersUrl) {

            if (trailerDownloadCnt >= configMax) {
                logger.debug(logMessage + "Downloaded maximum of " + configMax + (configMax == 1 ? " trailer" : " trailers"));
                break;
            }

            // Add the trailer URL to the movie
            MovieFile tmf = new MovieFile();
            tmf.setTitle("TRAILER-" + getTrailerTitle(trailerRealUrl));

            // Is the found trailer one of the types to download/link to?
            if (!isValidTrailer(getFilenameFromUrl(trailerRealUrl))) {
                logger.debug(logMessage + "Trailer skipped: " + getFilenameFromUrl(trailerRealUrl));
                continue; // Quit the rest of the trailer loop.
            }

            // Issue with the naming of URL for trailer download
            // See: http://www.hd-trailers.net/blog/how-to-download-hd-trailers-from-apple/
            trailerRealUrl = trailerRealUrl.replace("www.apple.com", configReplaceUrl);
            trailerRealUrl = trailerRealUrl.replace("images.apple.com", configReplaceUrl);
            trailerRealUrl = trailerRealUrl.replace("movies.apple.com", configReplaceUrl);

            logger.debug(logMessage + "Trailer found for " + movie.getBaseName() + " (" + getFilenameFromUrl(trailerRealUrl) + ")");
            trailerDownloadCnt++;

            // Check if we need to download the trailer, or just link to it
            if (configDownload) {
                String trailerAppleName = getFilenameFromUrl(trailerRealUrl);
                trailerAppleName = new String(trailerAppleName.substring(0, trailerAppleName.lastIndexOf('.')));
                isExchangeOk = downloadTrailer(movie, trailerRealUrl, trailerAppleName, tmf);
            } else {
                // Just link to the trailer
                int underscore = trailerRealUrl.lastIndexOf('_');
                if (underscore > 0 && new String(trailerRealUrl.substring(underscore + 1, underscore + 2)).equals("h")) {
                    // remove the "h" from the trailer url for streaming
                    trailerRealUrl = new String(trailerRealUrl.substring(0, underscore + 1)) + new String(trailerRealUrl.substring(underscore + 2));
                }
                tmf.setFilename(trailerRealUrl);
                movie.addExtraFile(new ExtraFile(tmf));
                isExchangeOk = true;
            }
        }

        return isExchangeOk;
    }

    @Override
    public String getName() {
        return "apple";
    }

    private String getTrailerPageUrl(String movieName) {
        String doubleQuoteComma = "\",";
        String titleKey = "\"title\":\"";
        String locationKey = "\"location\":\"";

        try {
            String searchURL = "http://trailers.apple.com/trailers/home/scripts/quickfind.php?callback=searchCallback&q="
                    + URLEncoder.encode(movieName, "UTF-8");

            String xml = webBrowser.request(searchURL);

            int index = 0;
            int endIndex;
            while (true) {
                index = xml.indexOf(titleKey, index);
                if (index == -1) {
                    break;
                }

                index += titleKey.length();

                endIndex = xml.indexOf(doubleQuoteComma, index);
                if (endIndex == -1) {
                    break;
                }

                String trailerTitle = decodeEscapeICU(new String(xml.substring(index, endIndex)));

                index = endIndex + doubleQuoteComma.length();

                index = xml.indexOf(locationKey, index);
                if (index == -1) {
                    break;
                }

                index += locationKey.length();

                endIndex = xml.indexOf(doubleQuoteComma, index);
                if (endIndex == -1) {
                    break;
                }

                String trailerLocation = decodeEscapeICU(new String(xml.substring(index, endIndex)));

                index = endIndex + doubleQuoteComma.length();

                if (trailerTitle.equalsIgnoreCase(movieName)) {
                    String trailerUrl;

                    int itmsIndex = trailerLocation.indexOf("itms://");
                    if (itmsIndex == -1) {
                        // Convert relative URL to absolute URL - some urls are already absolute, and some relative
                        trailerUrl = getAbsUrl("http://www.apple.com/trailers/", trailerLocation);
                    } else {
                        trailerUrl = "http" + new String(trailerLocation.substring(itmsIndex + 4));
                    }

                    return trailerUrl;
                }
            }

        } catch (IOException error) {
            logger.error(logMessage + "Failed retreiving trailer for movie : " + movieName);
            logger.error(SystemTools.getStackTrace(error));
            return Movie.UNKNOWN;
        }

        return Movie.UNKNOWN;
    }

    private void getTrailerSubUrl(String trailerPageUrl, Set trailersUrl) {
        try {

            String xml = webBrowser.request(trailerPageUrl);

            // Try to find the movie link on the main page
            getTrailerMovieUrl(xml, trailersUrl);

            // New URL
            String trailerPageUrlNew = trailerPageUrl.replace("//www.apple.com/", "//trailers.apple.com/");

            String trailerPageUrlWebInc = getAbsUrl(trailerPageUrlNew, "includes/playlists/web.inc");
            String xmlWebInc = getSubPage(trailerPageUrlWebInc);
            // Try to find the movie link on the WebInc page
            getTrailerMovieUrl(xmlWebInc, trailersUrl);

            // Search for the 'HD' Page
            String trailerPageUrlHD = getAbsUrl(trailerPageUrlNew, "hd");
            String xmlHD = getSubPage(trailerPageUrlHD);

            // Only search if the HD URL is valid
            if (StringTools.isValidString(xmlHD)) {
                // Try to find the movie link on the HD page
                getTrailerMovieUrl(xmlHD, trailersUrl);

                String trailerPageUrlHDWebInc = getAbsUrl(trailerPageUrlHD, "includes/playlists/web.inc");
                String xmlHDWebInc = getSubPage(trailerPageUrlHDWebInc);
                // Try to find the movie link on the WebInc HD page
                getTrailerMovieUrl(xmlHDWebInc, trailersUrl);
            } else {
                logger.debug(logMessage + "No valid HD URL found for " + trailerPageUrl);
            }
        } catch (IOException ex) {
            logger.error(logMessage + "Error : " + ex.getMessage());
            logger.error(SystemTools.getStackTrace(ex));
        }
    }

    // Get sub page url - if error return empty page
    private String getSubPage(String url) {
        Level oldlevel = logger.getLevel();

        try {
            // Don't log error getting URL
            logger.setLevel(Level.OFF);
            return webBrowser.request(url);
        } catch (Exception error) {
            return "";
        } finally {
            logger.setLevel(oldlevel);
        }
    }

    private void getTrailerMovieUrl(String xml, Set trailersUrl) {
        Matcher m = TRAILER_URL_PATTERN.matcher(xml);
        while (m.find()) {
            String movieUrl = m.group();
            trailersUrl.add(movieUrl);
        }
    }

    private void selectBestTrailer(Set trailersUrl, Set bestTrailersUrl) {

        boolean startSearch = false;

        for (String resolution : RESOLUTION_ARRAY) {
            if (configResolution.equals(resolution)) {
                startSearch = true;
            }
            if (startSearch) {
                for (String curURL : trailersUrl) {
                    // Search for a specific resolution
                    if (curURL.indexOf(resolution) != -1) {
                        addTailerRealUrl(bestTrailersUrl, curURL);
                    }
                }
            }

            if (!bestTrailersUrl.isEmpty()) {
                break;
            }
        }
    }

    private void addTailerRealUrl(Set bestTrailersUrl, String trailerUrl) {
        String trailerRealUrl = getTrailerRealUrl(trailerUrl);
        bestTrailersUrl.add(trailerRealUrl);
    }

    private String getTrailerRealUrl(String trailerUrl) {
        try {
            URL url = new URL(trailerUrl);
            HttpURLConnection connection = (HttpURLConnection) (url.openConnection());
            InputStream inputStream = connection.getInputStream();

            byte buf[] = new byte[1024];
            int len;
            len = inputStream.read(buf);

            // Check if too much data read, that this is the real url already
            if (len == 1024) {
                return trailerUrl;
            }

            String mov = new String(buf);

            int pos = 44;
            StringBuilder realUrl = new StringBuilder();

            while (mov.charAt(pos) != 0) {
                realUrl.append(mov.charAt(pos));

                pos++;
            }

            String absRealURL = getAbsUrl(trailerUrl, realUrl.toString());

            return absRealURL;

        } catch (Exception error) {
            logger.error(logMessage + "Error : " + error.getMessage());
            logger.error(SystemTools.getStackTrace(error));
            return Movie.UNKNOWN;
        }
    }

    private String getTrailerTitle(String url) {
        int start = url.lastIndexOf('/');
        int end = url.indexOf(".mov", start);

        if ((start == -1) || (end == -1)) {
            return Movie.UNKNOWN;
        }

        StringBuilder title = new StringBuilder();

        for (int i = start + 1; i < end; i++) {
            if ((url.charAt(i) == '-') || (url.charAt(i) == '_')) {
                title.append(' ');
            } else {
                if (i == start + 1) {
                    title.append(Character.toUpperCase(url.charAt(i)));
                } else {
                    title.append(url.charAt(i));
                }
            }
        }

        return title.toString();
    }

    private String getAbsUrl(String baseUrl, String relativeUrl) {
        try {
            URL baseURL = new URL(baseUrl);
            URL absURL = new URL(baseURL, relativeUrl);
            return absURL.toString();
        } catch (Exception error) {
            return Movie.UNKNOWN;
        }
    }

    private String decodeEscapeICU(String str) {
        StringBuilder newString = new StringBuilder();

        int loop = 0;
        while (loop < str.length()) {
            // Check ICU escaping
            if ((str.charAt(loop) == '%') && (loop + 5 < str.length()) && (str.charAt(loop + 1) == 'u')) {

                String value = new String(str.substring(loop + 2, loop + 6));
                int intValue = Integer.parseInt(value, 16);

                // fix for ' char
                if (intValue == 0x2019) {
                    intValue = 0x0027;
                }

                char c = (char) intValue;

                newString.append(c);
                loop += 6;
            } else {
                if (str.charAt(loop) == '\\') {
                    loop++;
                } else {
                    newString.append(str.charAt(loop));
                    loop++;
                }
            }
        }

        return newString.toString();
    }

    // Extract the filename from the URL
    private String getFilenameFromUrl(String fullUrl) {
        int nameStart = fullUrl.lastIndexOf('/') + 1;
        return new String(fullUrl.substring(nameStart));
    }

    // Check the trailer filename against the valid trailer types from appletrailers.trailertypes
    private boolean isValidTrailer(String trailerFilename) {
        boolean validTrailer;

        if (configTypesInclude) {
            validTrailer = false;
        } else {
            validTrailer = true;
        }

        for (String ttype : configTrailerTypes.split(",")) {
            if (trailerFilename.lastIndexOf(ttype) > 0) {
                if (configTypesInclude) {
                    // Found the trailer type, so this is a valid trailer
                    validTrailer = true;
                } else {
                    // Found the trailer type, so this trailer should be excluded
                    validTrailer = false;
                }
                break;
            }
        }

        return validTrailer;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy