Browse Source

Merging qBittorrent adapter fixes with support for 3.2+, added proper version number retrieval.

material
Eric Kok 9 years ago
parent
commit
5d68d06122
  1. 112
      app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java

112
app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java

@ -17,24 +17,18 @@
*/ */
package org.transdroid.daemon.Qbittorrent; package org.transdroid.daemon.Qbittorrent;
import java.io.File; import com.android.internalcopy.http.multipart.FilePart;
import java.io.FileNotFoundException; import com.android.internalcopy.http.multipart.MultipartEntity;
import java.io.UnsupportedEncodingException; import com.android.internalcopy.http.multipart.Part;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.cookie.Cookie;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@ -42,6 +36,7 @@ import org.json.JSONObject;
import org.transdroid.core.gui.log.Log; import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon; import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings; import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Priority; import org.transdroid.daemon.Priority;
@ -49,7 +44,6 @@ import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentDetails; import org.transdroid.daemon.TorrentDetails;
import org.transdroid.daemon.TorrentFile; import org.transdroid.daemon.TorrentFile;
import org.transdroid.daemon.TorrentStatus; import org.transdroid.daemon.TorrentStatus;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask; import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask; import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask; import org.transdroid.daemon.task.AddByUrlTask;
@ -67,9 +61,15 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask; import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask; import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.HttpHelper; import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity; import java.io.File;
import com.android.internalcopy.http.multipart.Part; import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** /**
* The daemon adapter for the qBittorrent torrent client. * The daemon adapter for the qBittorrent torrent client.
@ -89,36 +89,40 @@ public class QbittorrentAdapter implements IDaemonAdapter {
} }
private synchronized void ensureVersion(Log log) throws DaemonException { private synchronized void ensureVersion(Log log) throws DaemonException {
if (version > 0) // Still need to retrieve the API and qBittorrent version numbers from the server?
if (version > 0 && apiVersion > 0)
return; return;
// We still need to retrieve the version number from the server
// Do this by getting the web interface about page and trying to parse the version number
// Format is something like 'qBittorrent v2.9.7 (Web UI)'
try {
// The API version is only supported since qBittorrent 3.2, so otherwise we assume version 1
try { try {
String apiVerText = makeRequest(log, "/version/api"); String apiVerText = makeRequest(log, "/version/api");
apiVersion = Integer.parseInt(apiVerText.trim()); apiVersion = Integer.parseInt(apiVerText.trim());
} } catch (DaemonException | NumberFormatException e) {
catch (DaemonException e) {
apiVersion = 1; apiVersion = 1;
} }
catch (NumberFormatException e) {
apiVersion = 1;
}
log.d(LOG_NAME, "qBittorrent API version is " + apiVersion); log.d(LOG_NAME, "qBittorrent API version is " + apiVersion);
// TODO: In API ver 2, query this information from /version/qbittorrent instead. // The qBittorent version is only supported since 3.2; for earlier versions we parse the about dialog and parse it
// For now at least this works fine, though String versionText = "";
if (apiVersion > 1) {
// Format is something like 'v3.2.0'
versionText = makeRequest(log, "/version/qbittorrent").substring(1);
} else {
// Format is something like 'qBittorrent v2.9.7 (Web UI)' or 'qBittorrent v3.0.0-alpha5 (Web UI)'
String about = makeRequest(log, "/about.html"); String about = makeRequest(log, "/about.html");
String aboutStartText = "qBittorrent v"; String aboutStartText = "qBittorrent v";
String aboutEndText = " (Web UI)"; String aboutEndText = " (Web UI)";
int aboutStart = about.indexOf(aboutStartText); int aboutStart = about.indexOf(aboutStartText);
int aboutEnd = about.indexOf(aboutEndText); int aboutEnd = about.indexOf(aboutEndText);
try {
if (aboutStart >= 0 && aboutEnd > aboutStart) { if (aboutStart >= 0 && aboutEnd > aboutStart) {
versionText = about.substring(aboutStart + aboutStartText.length(), aboutEnd);
}
}
// String found: now parse a version like 2.9.7 as a number like 20907 (allowing 10 places for each .) // String found: now parse a version like 2.9.7 as a number like 20907 (allowing 10 places for each .)
String[] parts = about.substring(aboutStart + aboutStartText.length(), aboutEnd).split("\\."); String[] parts = versionText.split("\\.");
if (parts.length > 0) { if (parts.length > 0) {
version = Integer.parseInt(parts[0]) * 100 * 100; version = Integer.parseInt(parts[0]) * 100 * 100;
if (parts.length > 1) { if (parts.length > 1) {
@ -141,14 +145,15 @@ public class QbittorrentAdapter implements IDaemonAdapter {
} }
} }
} }
}
} catch (NumberFormatException e) { } catch (Exception e) {
}
// Unable to establish version number; assume an old version by setting it to version 1 // Unable to establish version number; assume an old version by setting it to version 1
version = 10000; version = 10000;
apiVersion = 1; apiVersion = 1;
} }
}
private synchronized void ensureAuthenticated(Log log) throws DaemonException { private synchronized void ensureAuthenticated(Log log) throws DaemonException {
// API changed in 3.2.0, login is now handled by its own request, which provides you a cookie. // API changed in 3.2.0, login is now handled by its own request, which provides you a cookie.
// If we don't have that cookie, let's try and get it. // If we don't have that cookie, let's try and get it.
@ -166,8 +171,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
} }
} }
makeRequest(log, "/login", makeRequest(log, "/login", new BasicNameValuePair("username", settings.getUsername()),
new BasicNameValuePair("username", settings.getUsername()),
new BasicNameValuePair("password", settings.getPassword())); new BasicNameValuePair("password", settings.getPassword()));
// The HttpClient will automatically remember the cookie for us, no need to parse it out. // The HttpClient will automatically remember the cookie for us, no need to parse it out.
@ -210,17 +214,16 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Request tracker and error details for a specific teacher // Request tracker and error details for a specific teacher
String mhash = task.getTargetTorrent().getUniqueID(); String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages = new JSONArray(makeRequest(log, JSONArray messages =
(version >= 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/") + mhash)); new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/") + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(messages));
parseJsonTorrentDetails(messages));
case GetFileList: case GetFileList:
// Request files listing for a specific torrent // Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID(); String fhash = task.getTargetTorrent().getUniqueID();
JSONArray files = new JSONArray(makeRequest(log, JSONArray files =
(version >= 30200 ? "/query/propertiesFiles/" : "/json/propertiesFiles/") + fhash)); new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesFiles/" : "/json/propertiesFiles/") + fhash));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files)); return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files));
case AddByFile: case AddByFile:
@ -290,9 +293,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
} }
// We have to make a separate request per file, it seems // We have to make a separate request per file, it seems
for (TorrentFile file : setPrio.getForFiles()) { for (TorrentFile file : setPrio.getForFiles()) {
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent() makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()),
.getUniqueID()), new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair( new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair("priority", newPrio));
"priority", newPrio));
} }
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
@ -308,13 +310,12 @@ public class QbittorrentAdapter implements IDaemonAdapter {
JSONObject prefs = new JSONObject(makeRequest(log, "/json/preferences")); JSONObject prefs = new JSONObject(makeRequest(log, "/json/preferences"));
prefs.put("dl_limit", dl); prefs.put("dl_limit", dl);
prefs.put("up_limit", ul); prefs.put("up_limit", ul);
makeRequest(log, "/command/setPreferences", makeRequest(log, "/command/setPreferences", new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
return new DaemonTaskSuccessResult(task); return new DaemonTaskSuccessResult(task);
default: default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, return new DaemonTaskFailureResult(task,
task.getMethod() + " is not supported by " + getType())); new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
} }
} catch (JSONException e) { } catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString())); return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -331,7 +332,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Setup request using POST // Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path)); HttpPost httppost = new HttpPost(buildWebUIUrl(path));
List<NameValuePair> nvps = new ArrayList<NameValuePair>(); List<NameValuePair> nvps = new ArrayList<>();
Collections.addAll(nvps, params); Collections.addAll(nvps, params);
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
return makeWebRequest(httppost, log); return makeWebRequest(httppost, log);
@ -349,7 +350,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Setup request using POST // Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path)); HttpPost httppost = new HttpPost(buildWebUIUrl(path));
File upload = new File(URI.create(file)); File upload = new File(URI.create(file));
Part[] parts = { new FilePart("torrentfile", upload) }; Part[] parts = {new FilePart("torrentfile", upload)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams())); httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
return makeWebRequest(httppost, log); return makeWebRequest(httppost, log);
@ -414,8 +415,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private TorrentDetails parseJsonTorrentDetails(JSONArray messages) throws JSONException { private TorrentDetails parseJsonTorrentDetails(JSONArray messages) throws JSONException {
ArrayList<String> trackers = new ArrayList<String>(); ArrayList<String> trackers = new ArrayList<>();
ArrayList<String> errors = new ArrayList<String>(); ArrayList<String> errors = new ArrayList<>();
// Parse response // Parse response
if (messages.length() > 0) { if (messages.length() > 0) {
@ -436,7 +437,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private ArrayList<Torrent> parseJsonTorrents(JSONArray response) throws JSONException { private ArrayList<Torrent> parseJsonTorrents(JSONArray response) throws JSONException {
// Parse response // Parse response
ArrayList<Torrent> torrents = new ArrayList<Torrent>(); ArrayList<Torrent> torrents = new ArrayList<>();
for (int i = 0; i < response.length(); i++) { for (int i = 0; i < response.length(); i++) {
JSONObject tor = response.getJSONObject(i); JSONObject tor = response.getJSONObject(i);
int leechers[] = parsePeers(tor.getString("num_leechs")); int leechers[] = parsePeers(tor.getString("num_leechs"));
@ -533,10 +534,9 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// In some situations it it just a "6" string // In some situations it it just a "6" string
String[] parts = seeds.split(" "); String[] parts = seeds.split(" ");
if (parts.length > 1) { if (parts.length > 1) {
return new int[] { Integer.parseInt(parts[0]), return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1].substring(1, parts[1].length() - 1))};
Integer.parseInt(parts[1].substring(1, parts[1].length() - 1)) };
} }
return new int[] { Integer.parseInt(parts[0]), Integer.parseInt(parts[0]) }; return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[0])};
} }
private int parseSpeed(String speed) { private int parseSpeed(String speed) {
@ -612,8 +612,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
size = parseSize(file.getString("size")); size = parseSize(file.getString("size"));
} }
torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file.getDouble("progress")),
.getDouble("progress")), parsePriority(file.getInt("priority")))); parsePriority(file.getInt("priority"))));
} }
// Return the list // Return the list

Loading…
Cancel
Save