it.gnappuraz.netflixsubs.opensubtitles.api.OpenSubtitlesAPIClient.java Source code

Java tutorial

Introduction

Here is the source code for it.gnappuraz.netflixsubs.opensubtitles.api.OpenSubtitlesAPIClient.java

Source

/*
 *  Copyright 2011 daniele.belletti@gmail.com.
 *
 *  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.
 *  under the License.
 */

package it.gnappuraz.netflixsubs.opensubtitles.api;

import it.gnappuraz.netflixsubs.types.SubtitleEntry;
import it.gnappuraz.netflixsubs.types.SubtitleLink;
import org.apache.log4j.Logger;
import org.apache.ws.commons.util.Base64;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.zip.GZIPInputStream;

/**
 *
 * @author daniele.belletti@gmail.com
 */
@SuppressWarnings("ALL")
@Service
public class OpenSubtitlesAPIClient {

    private static final Logger log = Logger.getLogger(OpenSubtitlesAPIClient.class);
    private static final String USER_AGENT = "jOSAPI 1.0";
    //private static final String USER_AGENT = "OS Test User Agent";
    private static final String END_POINT = "http://api.opensubtitles.org/xml-rpc";
    private static final String LANGUAGE = "it";
    private XmlRpcClient client;
    private String token;

    @PostConstruct
    private void postConstruct() {
        client = new XmlRpcClient();
        XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
        try {
            config.setServerURL(new URL(END_POINT));
        } catch (MalformedURLException e) {
            log.error("MalformedURLException " + END_POINT, e);
        }
        client.setConfig(config);
        client.setTypeFactory(new TypeFactory(client));

        try {
            token = login("", "");
            log.info("Logged in with token:" + token);
        } catch (OpenSubtitlesException e) {
            log.fatal("Cannot login", e);
        }
    }

    private String login(String username, String password) throws OpenSubtitlesException {
        List<String> params = new ArrayList<String>();
        params.add(username);
        params.add(password);
        params.add(LANGUAGE);
        params.add(USER_AGENT);
        Map<String, Object> result = executeAPI(API.LOGIN, params);
        if (result.get("token") == null) {
            throw new OpenSubtitlesException("login error: token is null");
        }
        String token = (String) result.get("token");
        return token;
    }

    @PreDestroy
    private void logout() throws OpenSubtitlesException {
        List<String> params = new ArrayList<String>();
        params.add(token);
        executeAPI(API.LOGOUT, params);
    }

    public List<SubtitleEntry> search(File movie, Language language) throws OpenSubtitlesException {
        Language[] languageses = { language };
        return search(movie, languageses);
    }

    public List<SubtitleEntry> search(String title, String season, String episode, Language language)
            throws OpenSubtitlesException {
        Language[] languageses = { language };
        return search(title, season, episode, languageses);
    }

    public List<SubtitleEntry> search(String title, String season, String episode, Language[] languageses)
            throws OpenSubtitlesException {

        List<Object> request = APIRequestBuilder.create(token).addLanguages(languageses).addTitle(title)
                .addSeason(season).addEpisode(episode).getRequest();

        Map<String, Object> response = executeAPI(API.SEARCH, request);
        return convertResponse(response);
    }

    public List<SubtitleEntry> search(File movie, Language[] languageses) throws OpenSubtitlesException {
        try {
            List<Object> request = APIRequestBuilder.create(token).addLanguages(languageses).addMovieHash(movie)
                    .getRequest();
            Map<String, Object> response = executeAPI(API.SEARCH, request);
            return convertResponse(response);

        } catch (IOException e) {
            throw new OpenSubtitlesException("search error", e);
        }
    }

    private List<SubtitleEntry> convertResponse(Map<String, Object> response) {

        List<SubtitleEntry> results = new ArrayList<SubtitleEntry>();

        Object resObject = (Object) response.get("data");
        if (resObject instanceof Object[]) {
            Object[] data = (Object[]) resObject;
            for (Object o : data) {

                Map<String, Object> entry = (Map<String, Object>) o;
                //TODO add support for multi language
                SubtitleLink lnk = new SubtitleLink((String) entry.get("SubLanguageID"),
                        (String) entry.get("LanguageName"), (String) entry.get("IDSubtitleFile"));

                SubtitleEntry e = new SubtitleEntry((String) entry.get("SubFileName"), "www.opensubtitles.org",
                        Arrays.asList(lnk));

                results.add(e);
            }
        } else if (resObject instanceof Boolean) {
            Boolean result = (Boolean) resObject;
            if (!result) {
                log.info("No results found");
            }
        } else {
            throw new RuntimeException("Not know object type: " + resObject.getClass());
        }

        return results;
    }

    public Map<Integer, byte[]> download(Integer... id) throws OpenSubtitlesException {
        Map<Integer, byte[]> result = new HashMap<Integer, byte[]>();
        List<Object> params = new ArrayList<Object>();
        List<Integer> ids = new ArrayList<Integer>();
        ids.addAll(Arrays.asList(id));
        params.add(token);
        params.add(ids);
        Map<String, Object> temp = executeAPI(API.DOWNLOAD, params);
        Object[] data = (Object[]) temp.get("data");
        try {
            for (Object o : data) {
                temp = (Map<String, Object>) o;
                byte[] decodedBytes = base64decode((String) temp.get("data"));
                byte[] subtitle = gunzip(decodedBytes);
                result.put(new Integer(temp.get("idsubtitlefile") + ""), subtitle);
            }
        } catch (Exception e) {
            throw new OpenSubtitlesException("download error", e);
        }
        return result;
    }

    private byte[] base64decode(String encoded) throws IOException {
        byte[] output = Base64.decode(encoded);
        return output;
    }

    private byte[] gunzip(byte[] compressed) throws IOException {
        GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(compressed));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OutputStream out = new BufferedOutputStream(baos);
        byte[] buffer = new byte[1024];
        while (true) {
            synchronized (buffer) {
                int amountRead = gis.read(buffer);
                if (amountRead == -1) {
                    break;
                }
                out.write(buffer, 0, amountRead);
            }
        }
        out.flush();
        out.close();
        return baos.toByteArray();
    }

    private Map<String, Object> executeAPI(API api, List<?> params) throws OpenSubtitlesException {
        Map<String, Object> result = null;
        try {
            result = (Map) client.execute(api.toString(), params);
            String statusString = (String) result.get("status");
            Status status = Status.fromCode(getCode(statusString));
            if (!status.isSuccess()) {
                throw new OpenSubtitlesException(statusString);
            }
        } catch (Exception ex) {
            throw new OpenSubtitlesException("excuteAPI error: " + api, ex);
        }
        return result;
    }

    private String getCode(String statusMessage) {
        return statusMessage.split(" ", 2)[0];
    }

    private String getMessage(String statusMessage) {
        return statusMessage.split(" ", 2)[1];
    }

    private enum API {

        LOGIN("LogIn"), SEARCH("SearchSubtitles"), DOWNLOAD("DownloadSubtitles"), LOGOUT("LogOut");
        private String functionName;

        API(String functionName) {
            this.functionName = functionName;
        }

        @Override
        public String toString() {
            return functionName;
        }
    }
}