com.sabdroidex.controllers.couchpotato.CouchPotatoController.java Source code

Java tutorial

Introduction

Here is the source code for com.sabdroidex.controllers.couchpotato.CouchPotatoController.java

Source

/*
 * Copyright (C) 2011-2013  Roy Kokkelkoren
 *
 * This program 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
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.sabdroidex.controllers.couchpotato;

import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.sabdroidex.data.couchpotato.MovieList;
import com.sabdroidex.data.couchpotato.MovieReleases;
import com.sabdroidex.data.couchpotato.MovieSearch;
import com.sabdroidex.utils.Preferences;
import com.sabdroidex.utils.json.impl.SimpleJSONMarshaller;
import com.utils.ApacheCredentialProvider;
import com.utils.HttpUtil;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.IOException;
import java.util.HashMap;

public final class CouchPotatoController {

    private static final String TAG = CouchPotatoController.class.getCanonicalName();

    private static boolean executingRefreshMovies = false;
    private static boolean executingCommand = false;

    private static HashMap<Integer, String> profiles;
    private static HashMap<Integer, String> status;
    public static boolean paused = false;

    private static final String URL_TEMPLATE = "[COUCHPOTATO_URL]/[COUCHPOTATO_URL_EXTENTION]api/[API]/[COMMAND]/";
    private static final String API_TEMPLATE = "[COUCHPOTATO_URL]/[COUCHPOTATO_URL_EXTENTION]getkey/?p=[PASSWORD]&u=[USERNAME]";

    public static enum MESSAGE {
        MOVIE_ADD, MOVIE_DELETE, MOVIE_EDIT, MOVIE_REFRESH, MOVIE_GET, MOVIE_LIST, MOVIE_SEARCH, PROFILE_LIST, UPDATE, STATUS_LIST, APP_RESTART, APP_SHUTDOWN, RELEASE_DELETE, RELEASE_IGNORE, RELEASE_DOWNLOAD, SEARCHER_TRY_NEXT, RELEASE_FOR_MOVIE
    }

    /**
     * This function handle the API calls to Sabnzbd to define the URL and
     * parameters
     * 
     * @param command The type of command that will be sent to Sabnzbd
     * @return The result of the API call
     * @throws RuntimeException
     *             Thrown if there is any unexpected problem during the
     *             communication with the server
     */
    public static String makeApiCall(String command) throws Exception {
        return makeApiCall(command, "");
    }

    /**
     * This function handle the API calls to Couchpotato to define the URL and
     * parameters
     * 
     * @param command The type of command that will be sent to Sabnzbd
     * @param extraParams Any parameter that will have to be part of the URL
     * @return The result of the API call
     * @throws RuntimeException
     *             Thrown if there is any unexpected problem during the
     *             communication with the server
     */
    public static String makeApiCall(String command, String... extraParams) throws Exception {

        /**
         * Correcting the command names to be understood by CouchPotato
         */
        command = command.replaceFirst("_", ".");
        String url = getFormattedUrl();
        url = url.replace("[COMMAND]", command);
        url = url.replace("[API]", Preferences.get(Preferences.COUCHPOTATO_API_KEY));

        for (final String xTraParam : extraParams) {
            if (xTraParam != null && !xTraParam.trim().equals("")) {
                url = url.endsWith("/") ? url + "?" + xTraParam : url + "&" + xTraParam;
            }
        }

        url = url.replace(" ", "%20");
        Log.d(TAG, url);

        return new String(
                HttpUtil.getInstance().getDataAsCharArray(url, ApacheCredentialProvider.getCredentialsProvider()));
    }

    /**
     * This function gets the URL used to connect to the CouchPotato server
     * 
     * @return url A {@link String} containing the URL of the CouchPotato server
     */
    private static String getFormattedUrl() {
        String url = URL_TEMPLATE;

        /**
         * Checking if there is a port to concatenate to the URL
         */
        if ("".equals(Preferences.get(Preferences.COUCHPOTATO_PORT))) {
            url = url.replace("[COUCHPOTATO_URL]", Preferences.get(Preferences.COUCHPOTATO_URL));
        } else {
            url = url.replace("[COUCHPOTATO_URL]", Preferences.get(Preferences.COUCHPOTATO_URL) + ":"
                    + Preferences.get(Preferences.COUCHPOTATO_PORT));
        }

        /**
         * Checking the url extention
         */
        if ("".equals(Preferences.get(Preferences.COUCHPOTATO_URL_EXTENTION))) {
            url = url.replace("[COUCHPOTATO_URL_EXTENTION]",
                    Preferences.get(Preferences.COUCHPOTATO_URL_EXTENTION));
        } else {
            url = url.replace("[COUCHPOTATO_URL_EXTENTION]",
                    Preferences.get(Preferences.COUCHPOTATO_URL_EXTENTION) + "/");
        }

        if (!url.toUpperCase().startsWith("HTTP://") && !url.toUpperCase().startsWith("HTTPS://")) {
            if (Preferences.isEnabled(Preferences.COUCHPOTATO_SSL)) {
                url = "https://" + url;
            } else {
                url = "http://" + url;
            }
        }

        return url;
    }

    /**
     * Add movie to couchpotato
     * 
     * @param messageHandler Handler
     * @param profile CouchPotato profile
     * @param idIMDb IMDB-ID
     * @param movieTitle Title of movie to add
     */
    public static void addMovie(final Handler messageHandler, final String profile, final String idIMDb,
            final String movieTitle) {
        if (executingCommand || !Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                try {
                    String result = makeApiCall(MESSAGE.MOVIE_ADD.toString().toLowerCase(), "profile_id=" + profile,
                            "identifier=" + idIMDb, "title=" + movieTitle);

                    JSONObject jsonObject = new JSONObject(result);
                    result = (String) jsonObject.get("added");

                    Message message = new Message();
                    message.setTarget(messageHandler);
                    message.what = MESSAGE.MOVIE_ADD.hashCode();
                    message.obj = result;
                    message.sendToTarget();

                    Thread.sleep(500);
                    CouchPotatoController.refreshMovies(messageHandler, "active,done");

                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                    sendUpdateMessageStatus(messageHandler, "Error");
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                    sendUpdateMessageStatus(messageHandler, "");
                }
            }
        };
        executingCommand = true;
        thread.start();
    }

    /**
     * Retrieve possible download profiles from CouchPotato
     */
    public synchronized static void getStatusList() {
        if ("".equals(Preferences.get(Preferences.COUCHPOTATO_URL))) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                try {
                    String result = makeApiCall(MESSAGE.STATUS_LIST.toString().toLowerCase());
                    JSONObject jsonObject = new JSONObject(result);

                    if (jsonObject.getBoolean("success")) {
                        JSONArray profileList = jsonObject.getJSONArray("list");
                        status = new HashMap<Integer, String>();
                        for (int i = 0; i < profileList.length(); i++) {
                            status.put(profileList.getJSONObject(i).getInt("id"),
                                    profileList.getJSONObject(i).getString("label"));
                        }
                    }
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                }
            }
        };
        executingCommand = true;
        thread.start();
    }

    /**
     * Retrieve possible download profiles from CouchPotato
     * 
     * @param messageHandler Handler
     */
    public synchronized static void getStatusList(final Handler messageHandler) {
        if (executingCommand || "".equals(Preferences.get(Preferences.COUCHPOTATO_URL))) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                try {
                    String result = makeApiCall(MESSAGE.STATUS_LIST.toString().toLowerCase());

                    JSONObject jsonObject = new JSONObject(result);

                    if (jsonObject.getBoolean("success")) {
                        JSONArray profileList = jsonObject.getJSONArray("list");
                        status = new HashMap<Integer, String>();
                        for (int i = 0; i < profileList.length(); i++) {
                            status.put(profileList.getJSONObject(i).getInt("id"),
                                    profileList.getJSONObject(i).getString("label"));
                        }

                        Message message = new Message();
                        message.setTarget(messageHandler);
                        message.what = MESSAGE.PROFILE_LIST.hashCode();
                        message.obj = status;
                        message.sendToTarget();
                    }
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                }
            }
        };
        executingCommand = true;
        thread.start();
    }

    /**
     * Retrieve possible download profiles from CouchPotato
     */
    public synchronized static void getProfiles() {
        if ("".equals(Preferences.get(Preferences.COUCHPOTATO_URL))) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                try {
                    String result = makeApiCall(MESSAGE.PROFILE_LIST.toString().toLowerCase());
                    JSONObject jsonObject = new JSONObject(result);

                    if (jsonObject.getBoolean("success")) {
                        profiles = new HashMap<Integer, String>();
                        JSONArray profileList = jsonObject.getJSONArray("list");
                        for (int i = 0; i < profileList.length(); i++) {
                            profiles.put(profileList.getJSONObject(i).getInt("id"),
                                    profileList.getJSONObject(i).getString("label"));
                        }
                    }
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                }
            }
        };
        executingCommand = true;
        thread.start();
    }

    /**
     * Retrieve possible download profiles from CouchPotat
     * 
     * @param messageHandler Handler
     */
    public synchronized static void getProfiles(final Handler messageHandler) {
        if (executingCommand || !Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                try {
                    String result = makeApiCall(MESSAGE.PROFILE_LIST.toString().toLowerCase());

                    JSONObject jsonObject = new JSONObject(result);

                    if (jsonObject.getBoolean("success") && profiles == null) {
                        profiles = new HashMap<Integer, String>();
                        JSONArray profileList = jsonObject.getJSONArray("list");
                        for (int i = 0; i < profileList.length(); i++) {
                            profiles.put(profileList.getJSONObject(i).getInt("id"),
                                    profileList.getJSONObject(i).getString("label"));
                        }
                    }

                    Message message = new Message();
                    message.setTarget(messageHandler);
                    message.what = MESSAGE.PROFILE_LIST.hashCode();
                    message.obj = profiles;
                    message.sendToTarget();
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                }
            }
        };

        executingCommand = true;
        sendUpdateMessageStatus(messageHandler, MESSAGE.MOVIE_DELETE.toString());
        thread.start();
    }

    /**
     * This function refreshes the elements from movies.
     * 
     * @param messageHandler The message handler that will receive the result
     * @param status The status we want to fetch
     */
    public static void refreshMovies(final Handler messageHandler, final String status) {
        if (executingRefreshMovies || !Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }
        if (status == null) {
            getStatusList();
        }
        if (profiles == null) {
            getProfiles();
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                try {

                    String result = makeApiCall(MESSAGE.MOVIE_LIST.toString().toLowerCase(), "status=" + status);
                    JSONObject jsonObject = new JSONObject(result);
                    MovieList movieList;

                    if (!jsonObject.isNull("message") && !"".equals(jsonObject.getString("message"))) {
                        sendUpdateMessageStatus(messageHandler, "SickBeard : " + jsonObject.getString("message"));
                    } else {
                        SimpleJSONMarshaller jsonMarshaller = new SimpleJSONMarshaller(MovieList.class);
                        movieList = (MovieList) jsonMarshaller.unMarshal(jsonObject);

                        Message message = new Message();
                        message.setTarget(messageHandler);
                        message.what = MESSAGE.MOVIE_LIST.hashCode();
                        message.obj = movieList;
                        message.sendToTarget();
                    }
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getMessage());
                } finally {
                    executingRefreshMovies = false;
                    executingCommand = false;
                }
            }
        };
        executingCommand = true;
        thread.start();
    }

    /**
     * Delete a move Based on ID
     * 
     * @param messageHandler Handler
     * @param ids ID's of movies to delete
     */
    public static void deleteMovie(final Handler messageHandler, final int... ids) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                String movieIds = "";

                for (int i = 0; i < ids.length; i++) {
                    if (i == 0)
                        movieIds += Integer.toString(ids[i]);
                    else
                        movieIds += "," + Integer.toString(ids[i]);
                }

                try {
                    String result = makeApiCall(MESSAGE.MOVIE_DELETE.toString().toLowerCase(), "id=" + movieIds);
                    JSONObject jsonObject = new JSONObject(result);
                    if (jsonObject.getBoolean("success") && ids.length != 1) {
                        // TODO: Resource bundle
                        sendUpdateMessageStatus(messageHandler, "Deleted movies");
                    } else if (jsonObject.getBoolean("success") && ids.length == 1) {
                        // TODO: Resource bundle
                        sendUpdateMessageStatus(messageHandler, "Deleted movie");
                    } else {
                        // TODO: Resource bundle
                        sendUpdateMessageStatus(messageHandler, "Failed");
                    }

                    Thread.sleep(100);
                    CouchPotatoController.refreshMovies(messageHandler, "");

                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                    sendUpdateMessageStatus(messageHandler, "");
                }
            }
        };
        executingCommand = true;
        sendUpdateMessageStatus(messageHandler, MESSAGE.MOVIE_DELETE.toString());
        thread.start();
    }

    /**
     * Edit a move Based on ID and Profile_ID
     * 
     * @param messageHandler Handler
     * @param ids ID's of movies to edit
     * @param profile_id Profile ID of profile to edit the movies in
     */
    public static void editMovie(final Handler messageHandler, final int profile_id, final int... ids) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {
                String movieIds = "";

                for (int i = 0; i < ids.length; i++) {
                    if (i == 0)
                        movieIds += Integer.toString(ids[i]);
                    else
                        movieIds += "," + Integer.toString(ids[i]);
                }

                try {
                    String result = makeApiCall(MESSAGE.MOVIE_EDIT.toString().toLowerCase(),
                            "profile_id=" + profile_id, "id=" + movieIds);

                    JSONObject jsonObject = new JSONObject(result);
                    if (jsonObject.getBoolean("success")) {
                        // TODO: Resource bundle
                        sendUpdateMessageStatus(messageHandler, "Edited");
                    } else {
                        // TODO: Resource bundle
                        sendUpdateMessageStatus(messageHandler, "Failed");
                    }
                    Thread.sleep(100);
                    CouchPotatoController.refreshMovies(messageHandler, "active,done");

                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                    sendUpdateMessageStatus(messageHandler, "");
                }
            }
        };
        executingCommand = true;
        sendUpdateMessageStatus(messageHandler, MESSAGE.MOVIE_EDIT.toString());
        thread.start();
    }

    /**
     * Search for a movie based on title
     * 
     * @param messageHandler Handler
     * @param searchTitle Movie title to search for
     */
    public static void searchMovie(final Handler messageHandler, final String searchTitle) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {

                try {

                    String result = makeApiCall(MESSAGE.MOVIE_SEARCH.toString().toLowerCase(), "q=" + searchTitle);
                    JSONObject jsonObject = new JSONObject(result);
                    MovieSearch movieList = null;

                    if (!jsonObject.isNull("message") && !"".equals(jsonObject.getString("message"))) {
                        sendUpdateMessageStatus(messageHandler, "CouchPotato : " + jsonObject.getString("message"));
                    } else {
                        SimpleJSONMarshaller jsonMarshaller = new SimpleJSONMarshaller(MovieSearch.class);
                        movieList = (MovieSearch) jsonMarshaller.unMarshal(jsonObject);
                    }

                    Message message = new Message();
                    message.setTarget(messageHandler);
                    message.what = MESSAGE.MOVIE_SEARCH.hashCode();
                    message.obj = movieList;
                    message.sendToTarget();
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } finally {
                    executingCommand = false;
                }
            }
        };
        executingRefreshMovies = true;
        executingCommand = true;
        thread.start();
    }

    /**
     * This download the release information for a specific movie.
     *
     * @param messageHandler The message handler that will receive the result.
     * @param movieId The movie Id to get the information for.
     */
    public static void getReleasesForMovie(final Handler messageHandler, final int movieId) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {

                try {
                    String result = makeApiCall(MESSAGE.RELEASE_FOR_MOVIE.toString().toLowerCase(),
                            "id=" + movieId);
                    JSONObject jsonObject = new JSONObject(result);
                    MovieReleases movieReleases = null;

                    if (!jsonObject.isNull("message") && !"".equals(jsonObject.getString("message"))) {
                        sendUpdateMessageStatus(messageHandler, "CouchPotato : " + jsonObject.getString("message"));
                    } else {
                        SimpleJSONMarshaller jsonMarshaller = new SimpleJSONMarshaller(MovieReleases.class);
                        movieReleases = (MovieReleases) jsonMarshaller.unMarshal(jsonObject);
                    }

                    Message message = new Message();
                    message.setTarget(messageHandler);
                    message.what = MESSAGE.RELEASE_FOR_MOVIE.hashCode();
                    message.obj = movieReleases;
                    message.sendToTarget();
                } catch (RuntimeException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                }
            }
        };

        thread.start();
    }

    /**
     * Ignore a release
     * 
     * @param messageHandler Handler
     * @param releaseId ID of release to ignore
     */
    public static void ignoreRelease(final Handler messageHandler, final int releaseId) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {

                try {
                    String result = makeApiCall(MESSAGE.RELEASE_IGNORE.toString().toLowerCase(), "id=" + releaseId);
                    JSONObject jsonObject = new JSONObject(result);
                    Log.d(TAG, jsonObject.toString());

                } catch (RuntimeException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                }
            }
        };
        thread.start();
    }

    /**
     * Download a release of a movie
     * 
     * @param messageHandler Handler
     * @param releaseId ID of release to download
     */
    public static void downloadRelease(final Handler messageHandler, final int releaseId) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {

                try {
                    String result = makeApiCall(MESSAGE.RELEASE_DOWNLOAD.toString().toLowerCase(),
                            "id=" + releaseId);
                    JSONObject jsonObject = new JSONObject(result);

                    Message message = new Message();
                    message.setTarget(messageHandler);
                    message.what = MESSAGE.RELEASE_DOWNLOAD.hashCode();
                    message.obj = jsonObject.getBoolean("success");
                    message.sendToTarget();
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                }
            }
        };
        thread.start();
    }

    /**
     * Makes a call to mark the snatched results as ignored and try the next best release.
     *
     * @param messageHandler Handler
     * @param releaseId ID of release to download
     */
    public static void snatchNextMovieRelease(final Handler messageHandler, final int releaseId) {
        if (!Preferences.isSet(Preferences.COUCHPOTATO_URL)) {
            return;
        }

        Thread thread = new Thread() {

            @Override
            public void run() {

                try {
                    String result = makeApiCall(MESSAGE.SEARCHER_TRY_NEXT.toString().toLowerCase(),
                            "id=" + releaseId);
                    JSONObject jsonObject = new JSONObject(result);
                    Log.d(TAG, jsonObject.toString());
                } catch (IOException e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                } catch (Throwable e) {
                    Log.w(TAG, " " + e.getLocalizedMessage());
                }
            }
        };
        thread.start();
    }

    /**
     * Get Profile based on ID key
     * 
     * @param key Identifier of Profile
     * @return Profile
     */
    public synchronized static String getProfile(Integer key) {
        String result = "";
        if (profiles == null) {
            getProfiles();
        } else {
            result = profiles.get(key);
        }
        return result;
    }

    /**
     * Get Status based on ID key
     * 
     * @param key Identifier of status
     * @return Status
     */
    public synchronized static String getStatus(Integer key) {
        String result = "";
        if (status == null) {
            getStatusList();
        } else {
            result = status.get(key);
        }
        return result;
    }

    /**
     * Receive keyset from Profile List
     * 
     * @return Integer keyset from Profiles
     */
    public static HashMap<Integer, String> getAllProfiles() {
        return profiles;
    }

    /**
     * Sends a message to the calling {@link Activity} to update it's status bar
     * 
     * @param messageHandler The message handler to be notified
     * @param text The text to write in the message
     */
    private static void sendUpdateMessageStatus(Handler messageHandler, String text) {

        Message message = new Message();
        message.setTarget(messageHandler);
        message.what = MESSAGE.UPDATE.hashCode();
        message.obj = text;
        message.sendToTarget();
    }
}