/*
* 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();
}
}