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

com.moviejukebox.plugin.OpenSubtitlesPlugin Maven / Gradle / Ivy

The newest version!
/*
 *      Copyright (c) 2004-2013 YAMJ Members
 *      http://code.google.com/p/moviejukebox/people/list
 *
 *      This file is part of the Yet Another Movie Jukebox (YAMJ).
 *
 *      The 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 the YAMJ.  If not, see .
 *
 *      Web: http://code.google.com/p/moviejukebox/
 *
 */
package com.moviejukebox.plugin;

import com.moviejukebox.model.DirtyFlag;
import com.moviejukebox.model.Movie;
import com.moviejukebox.model.MovieFile;
import com.moviejukebox.tools.FileTools;
import com.moviejukebox.tools.PropertiesUtil;
import com.moviejukebox.tools.StringTools;
import com.moviejukebox.tools.SubtitleTools;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.util.Scanner;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

/**
 * Based on some code from the opensubtitles.org subtitle upload java applet
 */
public class OpenSubtitlesPlugin {

    private static final Logger logger = Logger.getLogger(OpenSubtitlesPlugin.class);
    private static final String LOG_MESSAGE = "OpenSubtitles Plugin: ";
    //private static String useragent = "Yet Another Movie Jukebox";
    private static final String OS_DB_SERVER = "http://api.opensubtitles.org/xml-rpc";
    private static String loginToken = "";
    // Literals
    private static final String OS_USER_AGENT = "moviejukebox 1.0.15";
    private static final String OS_METHOD_START = "";
    private static final String OS_METHOD_END = "";
    private static final String OS_PARAM = "";
    private static final String OS_MEMBER = "";
    private static final String OS_PARAMS = "";
    // Properties
    private static final boolean IS_ENABLED = PropertiesUtil.getBooleanProperty("opensubtitles.enable", Boolean.TRUE);
    private static final String SUB_LANGUAGE_ID = PropertiesUtil.getProperty("opensubtitles.language", "");
    private static String osUsername = PropertiesUtil.getProperty("opensubtitles.username", "");
    private static String osPassword = PropertiesUtil.getProperty("opensubtitles.password", "");

    static {
        if (IS_ENABLED) {
            // Check if subtitle language was selected
            if (StringUtils.isNotBlank(SUB_LANGUAGE_ID)) {
                // Part of opensubtitles.org protocol requirements
                logger.info(LOG_MESSAGE + "Subtitles service allowed by www.OpenSubtitles.org");

                // Login to opensubtitles.org system
                logIn();
            } else {
                logger.debug(LOG_MESSAGE + "No language selected in properties file");
            }
        } else {
            logger.trace(LOG_MESSAGE + "Plugin disabled.");
        }
    }

    public OpenSubtitlesPlugin() {
    }

    /**
     * Login to OpenSubtitles
     */
    private static void logIn() {
        try {
            if (StringUtils.isBlank(loginToken)) {
                String parm[] = {osUsername, osPassword, "", OS_USER_AGENT};
                String xml = generateXMLRPC("LogIn", parm);
                String ret = sendRPC(xml);
                getValue("status", ret);
                loginToken = getValue("token", ret);
                if (loginToken.equals("")) {
                    logger.error(LOG_MESSAGE + "Login error.\n" + ret);
                } else {
                    logger.debug(LOG_MESSAGE + "Login successful.");
                }
                // String l1 = login.equals("") ? "Anonymous" : login;
            }
        } catch (Exception ex) {
            logger.error(LOG_MESSAGE + "Login Failed: " + ex.getMessage());
        }
    }

    /**
     * Logout of OpenSubtitles
     */
    public static void logOut() {
        // Check if plugin is enabled, subtitle language was selected and that the login was successful
        if (IS_ENABLED && StringUtils.isNotBlank(SUB_LANGUAGE_ID) && StringUtils.isNotBlank(loginToken)) {
            try {
                String p1[] = {loginToken};
                String xml = generateXMLRPC("LogOut", p1);
                sendRPC(xml);
            } catch (Exception error) {
                logger.error(LOG_MESSAGE + "Logout Failed");
            }
        }
    }

    /**
     * Get subtitles for the video
     *
     * @param movie
     */
    public void generate(Movie movie) {
        if (!IS_ENABLED) {
            return;
        }

        if (StringTools.isNotValidString(movie.getSubtitles()) || movie.getSubtitles().equalsIgnoreCase("NO") || movie.isTVShow()) {
            // Check if subtitle language was selected
            if (StringUtils.isBlank(SUB_LANGUAGE_ID)) {
                return;
            }

            // Check to see if we scrape the library, if we don't then skip the download
            if (!movie.isScrapeLibrary()) {
                logger.debug(LOG_MESSAGE + "Skipped for " + movie.getTitle() + " due to scrape library flag");
                return;
            }

            // Check that the login was successful
            if (StringUtils.isBlank(loginToken)) {
                logger.debug(LOG_MESSAGE + "Login failed");
                return;
            }

            // Check if all files have subtitle
            boolean allSubtitleExist = Boolean.TRUE;
            boolean allSubtitleExchange = Boolean.TRUE;

            // Go over all the movie files and check subtitle status
            for (MovieFile mf : movie.getMovieFiles()) {

                if (!mf.isSubtitlesExchange()) {
                    allSubtitleExchange = Boolean.FALSE;
                }

                // Check if this movie already have subtitles for it
                if (mf.getFile() == null) {
                    // The file pointer doesn't exist, so skip the file
                    continue;
                }


                String path = mf.getFile().getAbsolutePath();
                int index = path.lastIndexOf('.');
                String basename = path.substring(0, index + 1);

                File subtitleFile = FileTools.fileCache.getFile(basename + "srt");

                if (!subtitleFile.exists()) {
                    allSubtitleExchange = Boolean.FALSE;
                    allSubtitleExist = Boolean.FALSE;
                    break;
                }
            }

            if (allSubtitleExchange) {
                logger.debug(LOG_MESSAGE + "All subtitles exist for " + movie.getTitle());
                // Don't return yet, we might want to upload the files.
                //return;
            }

            // Check if all files have subtitle , or this is a tv show, that each episode is by itself
            if (!allSubtitleExist || movie.getMovieType().equals(Movie.TYPE_TVSHOW)) {

                // Go over all the movie files
                for (MovieFile mf : movie.getMovieFiles()) {

                    // Check if this movie already have subtitles for it
                    String path = mf.getFile().getAbsolutePath();
                    int index = path.lastIndexOf('.');
                    String basename = path.substring(0, index + 1);

                    File subtitleFile = FileTools.fileCache.getFile(basename + "srt");

                    if (!subtitleFile.exists()) {
                        if (subtitleDownload(movie, mf.getFile(), subtitleFile) == Boolean.TRUE) {
                            movie.setDirty(DirtyFlag.INFO, Boolean.TRUE);
                            mf.setSubtitlesExchange(Boolean.TRUE);
                        }
                    } else {
                        if (!mf.isSubtitlesExchange()) {
                            File movieFileArray[] = new File[1];
                            File subtitleFileArray[] = new File[1];

                            movieFileArray[0] = mf.getFile();
                            subtitleFileArray[0] = subtitleFile;

                            if (subtitleUpload(movie, movieFileArray, subtitleFileArray) == Boolean.TRUE) {
                                movie.setDirty(DirtyFlag.INFO, Boolean.TRUE);
                                mf.setSubtitlesExchange(Boolean.TRUE);
                            }
                        }
                    }
                }
            } else {
                // Upload all movie files as a group
                File movieFileArray[] = new File[movie.getMovieFiles().size()];
                File subtitleFileArray[] = new File[movie.getMovieFiles().size()];
                int i = 0;

                // Go over all the movie files
                for (MovieFile mf : movie.getMovieFiles()) {

                    // Check if this movie already have subtitles for it
                    String path = mf.getFile().getAbsolutePath();
                    int index = path.lastIndexOf('.');
                    String basename = path.substring(0, index + 1);

                    File subtitleFile = new File(basename + "srt");

                    movieFileArray[i] = mf.getFile();
                    subtitleFileArray[i] = subtitleFile;
                    i++;
                }

                if (subtitleUpload(movie, movieFileArray, subtitleFileArray) == Boolean.TRUE) {
                    movie.setDirty(DirtyFlag.INFO, Boolean.TRUE);

                    // Go over all the movie files and mark the exchange
                    for (MovieFile mf : movie.getMovieFiles()) {
                        mf.setSubtitlesExchange(Boolean.TRUE);
                    }
                }
            }
        } else {
            logger.debug(LOG_MESSAGE + "Skipping subtitle download for " + movie.getTitle() + ", subtitles already exist: " + movie.getSubtitles());
        }
    }

    private boolean subtitleDownload(Movie movie, File movieFile, File subtitleFile) {
        try {
            String ret;
            String xml;

            String moviehash = getHash(movieFile);
            String moviebytesize = String.valueOf(movieFile.length());

            xml = generateXMLRPCSS(moviehash, moviebytesize);
            ret = sendRPC(xml);

            String subDownloadLink = getValue("SubDownloadLink", ret);

            if (subDownloadLink.equals("")) {
                String moviefilename = movieFile.getName();

                // Do not search by file name for BD rip files in the format 0xxxx.m2ts
                if (!(moviefilename.toUpperCase().endsWith(".M2TS") && moviefilename.startsWith("0"))) {

                    // Try to find the subtitle using file name
                    String subfilename = subtitleFile.getName();
                    int index = subfilename.lastIndexOf('.');

                    String query = subfilename.substring(0, index);

                    xml = generateXMLRPCSS(query);
                    ret = sendRPC(xml);

                    subDownloadLink = getValue("SubDownloadLink", ret);
                }
            }

            if (subDownloadLink.equals("")) {
                logger.debug(LOG_MESSAGE + "Subtitle not found for " + movieFile.getName());
                return Boolean.FALSE;
            }

            logger.debug(LOG_MESSAGE + "Download subtitle for " + movie.getBaseName());

            URL url = new URL(subDownloadLink);
            HttpURLConnection connection = (HttpURLConnection) (url.openConnection());
            connection.setRequestProperty("Connection", "Close");
            InputStream inputStream = connection.getInputStream();

            int code = connection.getResponseCode();
            if (code != HttpURLConnection.HTTP_OK) {
                logger.error(LOG_MESSAGE + "Download Failed");
                return Boolean.FALSE;
            }

            FileTools.copy(new GZIPInputStream(inputStream), new FileOutputStream(subtitleFile));
            connection.disconnect();

            String subLanguageID = getValue("SubLanguageID", ret);
            if (StringUtils.isNotBlank(subLanguageID)) {
                SubtitleTools.addMovieSubtitle(movie, subLanguageID);
            } else {
                SubtitleTools.addMovieSubtitle(movie, "YES");
            }

            return Boolean.TRUE;

        } catch (Exception error) {
            logger.error(LOG_MESSAGE + "Download Exception (Movie Not Found)");
            return Boolean.FALSE;
        }

    }

    private boolean subtitleUpload(Movie movie, File movieFile[], File subtitleFile[]) {
        ByteArrayOutputStream baos = null;
        DeflaterOutputStream deflaterOS = null;
        FileInputStream fisSubtitleFile = null;

        try {

            String ret;
            String xml;

            String idmovieimdb = movie.getId(ImdbPlugin.IMDB_PLUGIN_ID).substring(2);
            idmovieimdb = String.valueOf(Integer.parseInt(idmovieimdb));

            String subfilename[] = new String[movieFile.length];
            String subhash[] = new String[movieFile.length];
            String subcontent[] = new String[movieFile.length];

            String moviehash[] = new String[movieFile.length];
            String moviebytesize[] = new String[movieFile.length];
            String movietimems[] = new String[movieFile.length];
            String movieframes[] = new String[movieFile.length];
            String moviefps[] = new String[movieFile.length];
            String moviefilename[] = new String[movieFile.length];

            for (int i = 0; i < movieFile.length; i++) {

                subfilename[i] = subtitleFile[i].getName();
                subhash[i] = "";
                subcontent[i] = "";

                moviehash[i] = getHash(movieFile[i]);
                moviebytesize[i] = String.valueOf(movieFile[i].length());
                movietimems[i] = "";
                movieframes[i] = "";
                moviefps[i] = String.valueOf(movie.getFps());
                moviefilename[i] = movieFile[i].getName();

                fisSubtitleFile = new FileInputStream(subtitleFile[i]);
                MessageDigest md = MessageDigest.getInstance("MD5");
                byte s[] = new byte[fisSubtitleFile.available()];
                fisSubtitleFile.read(s);
                fisSubtitleFile.close();

                md.update(s);
                subhash[i] = hashstring(md.digest());
                baos = new ByteArrayOutputStream();
                deflaterOS = new DeflaterOutputStream(baos);
                deflaterOS.write(s);
                deflaterOS.finish();

                subcontent[i] = tuBase64(baos.toByteArray());
            }

            // Check if upload of this subtitle is required
            xml = generateXMLRPCTUS(subhash, subfilename, moviehash, moviebytesize, movietimems, movieframes, moviefps, moviefilename);

            ret = sendRPC(xml);

            String alreadyindb = getIntValue("alreadyindb", ret);

            if (!alreadyindb.equals("0")) {
                logger.debug(LOG_MESSAGE + "Subtitle already in db for " + movie.getBaseName());
                return Boolean.TRUE;
            }

            logger.debug(LOG_MESSAGE + "Upload Subtitle for " + movie.getBaseName());

            // Upload the subtitle
            xml = generateXMLRPCUS(idmovieimdb, subhash, subcontent, subfilename, moviehash, moviebytesize, movietimems, movieframes, moviefps, moviefilename);

            sendRPC(xml);

            return Boolean.TRUE;

        } catch (Exception ex) {
            logger.error(LOG_MESSAGE + "Upload Failed: " + ex.getMessage());
            return Boolean.FALSE;
        } finally {
            try {
                if (fisSubtitleFile != null) {
                    fisSubtitleFile.close();
                }
            } catch (IOException ex) {
                // Ignore
            }

            try {
                if (deflaterOS != null) {
                    deflaterOS.close();
                }
            } catch (IOException ex) {
                // Ignore
            }

            try {
                if (baos != null) {
                    baos.close();
                }
            } catch (IOException ex) {
                // Ignore
            }
        }
    }

    private static String generateXMLRPCSS(String moviehash, String moviebytesize) {
        StringBuilder sb = new StringBuilder(OS_METHOD_START);
        sb.append("SearchSubtitles");
        sb.append(OS_METHOD_END);
        sb.append(loginToken);
        sb.append(OS_PARAM);

        sb.append("");

        sb.append(addymember("sublanguageid", SUB_LANGUAGE_ID));
        sb.append(addymember("moviehash", moviehash));
        sb.append(addymember("moviebytesize", moviebytesize));

        sb.append(OS_MEMBER);

        sb.append(OS_PARAMS);
        return sb.toString();
    }

    private static String generateXMLRPCSS(String query) {
        StringBuilder sb = new StringBuilder(OS_METHOD_START);
        sb.append("SearchSubtitles");
        sb.append(OS_METHOD_END);
        sb.append(loginToken);
        sb.append(OS_PARAM);

        sb.append("");

        sb.append(addymember("sublanguageid", SUB_LANGUAGE_ID));
        sb.append(addymember("query", query));

        sb.append(OS_MEMBER);

        sb.append(OS_PARAMS);
        return sb.toString();
    }

    private static String generateXMLRPCUS(String idmovieimdb, String subhash[], String subcontent[], String subfilename[], String moviehash[],
            String moviebytesize[], String movietimems[], String movieframes[], String moviefps[], String moviefilename[]) {
        StringBuilder sb = new StringBuilder(OS_METHOD_START);
        sb.append("UploadSubtitles");
        sb.append(OS_METHOD_END);
        sb.append(loginToken);
        sb.append(OS_PARAM);

        for (int i = 0; i < subhash.length; i++) {
            sb.append("");
            sb.append("cd");
            sb.append(i + 1);
            sb.append("");
            sb.append(addymember("movietimems", movietimems[i]));
            sb.append(addymember("moviebytesize", moviebytesize[i]));
            sb.append(addymember("subfilename", subfilename[i]));
            sb.append(addymember("subcontent", subcontent[i]));
            sb.append(addymember("subhash", subhash[i]));
            sb.append(addymember("movieframes", movieframes[i]));
            sb.append(addymember("moviefps", moviefps[i]));
            sb.append(addymember("moviefilename", moviefilename[i]));
            sb.append(addymember("moviehash", moviehash[i]));

            sb.append(OS_MEMBER);
        }

        sb.append("baseinfo");
        sb.append(addymember("sublanguageid", SUB_LANGUAGE_ID));
        sb.append(addymember("idmovieimdb", idmovieimdb));
        sb.append(addymember("subauthorcomment", ""));
        sb.append(addymember("movieaka", ""));
        sb.append(addymember("moviereleasename", ""));

        sb.append(OS_MEMBER);

        sb.append(OS_PARAMS);
        return sb.toString();
    }

    private static String generateXMLRPCTUS(String subhash[], String subfilename[], String moviehash[], String moviebytesize[], String movietimems[],
            String movieframes[], String moviefps[], String moviefilename[]) {
        StringBuilder sb = new StringBuilder(OS_METHOD_START);
        sb.append("TryUploadSubtitles");
        sb.append(OS_METHOD_END);
        sb.append(loginToken);
        sb.append(OS_PARAM);

        for (int i = 0; i < subhash.length; i++) {
            sb.append("");
            sb.append("cd");
            sb.append(i + 1);
            sb.append("");
            sb.append(addymember("movietimems", movietimems[i]));
            sb.append(addymember("moviebytesize", moviebytesize[i]));
            sb.append(addymember("subfilename", subfilename[i]));
            sb.append(addymember("subhash", subhash[i]));
            sb.append(addymember("movieframes", movieframes[i]));
            sb.append(addymember("moviefps", moviefps[i]));
            sb.append(addymember("moviefilename", moviefilename[i]));
            sb.append(addymember("moviehash", moviehash[i]));

            sb.append(OS_MEMBER);
        }

        sb.append(OS_PARAMS);
        return sb.toString();
    }

    private static String sendRPC(String xml) throws IOException {

        StringBuilder str = new StringBuilder();
        String strona = OS_DB_SERVER;
        String logowanie = xml;
        URL url = new URL(strona);
        URLConnection connection = url.openConnection();
        connection.setRequestProperty("Connection", "Close");

        // connection.setRequestProperty("Accept","text/html");
        connection.setRequestProperty("Content-Type", "text/xml");
        connection.setDoOutput(Boolean.TRUE);

        connection.getOutputStream().write(logowanie.getBytes("UTF-8"));

        Scanner in;
        in = new Scanner(connection.getInputStream());

        while (in.hasNextLine()) {
            str.append(in.nextLine());
        }

        in.close();
        ((HttpURLConnection) connection).disconnect();

        return str.toString();
    }

    private static String getValue(String find, String xml) {
        int a = xml.indexOf(find);
        if (a != -1) {
            int b = xml.indexOf("", a);
            int c = xml.indexOf("", b);
            if ((b != -1) && (c != -1)) {
                return xml.substring(b + 8, c);
            }
        }
        return "";
    }

    private static String getIntValue(String find, String xml) {
        String str = "";
        int a = xml.indexOf(find);
        if (a != -1) {
            int b = xml.indexOf("", a);
            int c = xml.indexOf("", b);
            if ((b != -1) && (c != -1)) {
                return xml.substring(b + 5, c);
            }
        }
        return str;
    }

    private static String hashstring(byte[] arayhash) {
        StringBuilder str = new StringBuilder();
        String hex = "0123456789abcdef";
        for (int i = 0; i < arayhash.length; i++) {
            int m = arayhash[i] & 0xff;
            str.append(hex.charAt(m >> 4) + hex.charAt(m & 0xf));
        }
        return str.toString();
    }

    @SuppressWarnings("unused")
    private static String generateXMLRPCDetectLang(String body) {
        StringBuilder sb = new StringBuilder(OS_METHOD_START);
        sb.append("DetectLanguage");
        sb.append("");

        sb.append("");
        sb.append(loginToken);
        sb.append("");
        sb.append("");
        sb.append(body);
        sb.append(" ");

        sb.append("");
        return sb.toString();
    }

    private static String generateXMLRPC(String procname, String s[]) {
        StringBuilder str = new StringBuilder();
        str.append(OS_METHOD_START);
        str.append(procname).append("");

        for (int i = 0; i < s.length; i++) {
            str.append("").append(s[i]).append("");
        }

        str.append("");
        return str.toString();
    }

    private static String addEncje(String inputString) {
        String cleanString = inputString.replace("&", "&");
        cleanString = cleanString.replace("<", "<");
        cleanString = cleanString.replace(">", ">");
        cleanString = cleanString.replace("'", "'");
        cleanString = cleanString.replace("\"", """);
        return cleanString;
    }

    private static String tuBase64(byte s[]) {
        // You may use this for lower applet size
        // return new sun.misc.BASE64Encoder().encode(s);

        // char tx;
        // long mili = Calendar.getInstance().getTimeInMillis();
        String t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        char t2[] = t.toCharArray();
        char wynik[] = new char[(s.length / 3 + 1) * 4];

        int tri;
        for (int i = 0; i < (s.length / 3 + 1); i++) {
            tri = 0;
            int iii = i * 3;
            try {
                tri |= (s[iii] & 0xff) << 16;
                tri |= (s[iii + 1] & 0xff) << 8;
                tri |= (s[iii + 2] & 0xff);
            } catch (Exception error) {
            }

            for (int j = 0; j < 4; j++) {
                wynik[i * 4 + j] = (iii * 8 + j * 6 >= s.length * 8) ? '=' : t2[(tri >> 6 * (3 - j)) & 0x3f];
            }
            // if((i+1) % 19 ==0 ) str +="\n";
        }

        String str = new String(wynik);
        if (str.endsWith("====")) {
            str = str.substring(0, str.length() - 4);
        }

        return str;
    }

    private static String addymember(String name, String value) {
        return "" + name + "" + OpenSubtitlesPlugin.addEncje(value) + "";
    }

    private static String getHash(File f) throws IOException {
        // Open the file and then get a channel from the stream
        FileInputStream fis = new FileInputStream(f);
        FileChannel fc = fis.getChannel();
        long sz = fc.size();

        if (sz < 65536) {
            fc.close();
            fis.close();
            return "NoHash";
        }

        MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, 65536);
        long sum = sz;

        bb.order(ByteOrder.LITTLE_ENDIAN);
        for (int i = 0; i < 65536 / 8; i++) {
            sum += bb.getLong();// sum(bb);
        }
        bb = fc.map(FileChannel.MapMode.READ_ONLY, sz - 65536, 65536);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        for (int i = 0; i < 65536 / 8; i++) {
            sum += bb.getLong();// sum(bb);
        }
        sum = sum & 0xffffffffffffffffL;

        String s = String.format("%016x", sum);
        fc.close();
        fis.close();
        return s;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy