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. 346
      app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java

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

@ -17,24 +17,18 @@ @@ -17,24 +17,18 @@
*/
package org.transdroid.daemon.Qbittorrent;
import java.io.File;
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;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
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.json.JSONArray;
import org.json.JSONException;
@ -42,6 +36,7 @@ import org.json.JSONObject; @@ -42,6 +36,7 @@ import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Priority;
@ -49,7 +44,6 @@ import org.transdroid.daemon.Torrent; @@ -49,7 +44,6 @@ import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentDetails;
import org.transdroid.daemon.TorrentFile;
import org.transdroid.daemon.TorrentStatus;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
@ -67,9 +61,15 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult; @@ -67,9 +61,15 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
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.
@ -89,64 +89,69 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -89,64 +89,69 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
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;
// 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 {
String apiVerText = makeRequest(log, "/version/api");
apiVersion = Integer.parseInt(apiVerText.trim());
}
catch (DaemonException e) {
apiVersion = 1;
}
catch (NumberFormatException e) {
apiVersion = 1;
}
log.d(LOG_NAME, "qBittorrent API version is " + apiVersion);
// The API version is only supported since qBittorrent 3.2, so otherwise we assume version 1
try {
String apiVerText = makeRequest(log, "/version/api");
apiVersion = Integer.parseInt(apiVerText.trim());
} catch (DaemonException | NumberFormatException e) {
apiVersion = 1;
}
log.d(LOG_NAME, "qBittorrent API version is " + apiVersion);
// TODO: In API ver 2, query this information from /version/qbittorrent instead.
// For now at least this works fine, though
String about = makeRequest(log, "/about.html");
String aboutStartText = "qBittorrent v";
String aboutEndText = " (Web UI)";
int aboutStart = about.indexOf(aboutStartText);
int aboutEnd = about.indexOf(aboutEndText);
try {
if (aboutStart >= 0 && aboutEnd > aboutStart) {
// 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("\\.");
if (parts.length > 0) {
version = Integer.parseInt(parts[0]) * 100 * 100;
if (parts.length > 1) {
version += Integer.parseInt(parts[1]) * 100;
if (parts.length > 2) {
// For the last part only read until a non-numeric character is read
// For example version 3.0.0-alpha5 is read as version code 30000
String numbers = "";
for (char c : parts[2].toCharArray()) {
if (Character.isDigit(c))
// Still a number; add it to the numbers string
numbers += Character.toString(c);
else {
// No longer reading numbers; stop reading
break;
}
// The qBittorent version is only supported since 3.2; for earlier versions we parse the about dialog and parse it
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 aboutStartText = "qBittorrent v";
String aboutEndText = " (Web UI)";
int aboutStart = about.indexOf(aboutStartText);
int aboutEnd = about.indexOf(aboutEndText);
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[] parts = versionText.split("\\.");
if (parts.length > 0) {
version = Integer.parseInt(parts[0]) * 100 * 100;
if (parts.length > 1) {
version += Integer.parseInt(parts[1]) * 100;
if (parts.length > 2) {
// For the last part only read until a non-numeric character is read
// For example version 3.0.0-alpha5 is read as version code 30000
String numbers = "";
for (char c : parts[2].toCharArray()) {
if (Character.isDigit(c))
// Still a number; add it to the numbers string
numbers += Character.toString(c);
else {
// No longer reading numbers; stop reading
break;
}
version += Integer.parseInt(numbers);
return;
}
version += Integer.parseInt(numbers);
return;
}
}
}
} catch (NumberFormatException e) {
} catch (Exception e) {
// Unable to establish version number; assume an old version by setting it to version 1
version = 10000;
apiVersion = 1;
}
// Unable to establish version number; assume an old version by setting it to version 1
version = 10000;
apiVersion = 1;
}
private synchronized void ensureAuthenticated(Log log) throws DaemonException {
@ -166,8 +171,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -166,8 +171,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
}
makeRequest(log, "/login",
new BasicNameValuePair("username", settings.getUsername()),
makeRequest(log, "/login", new BasicNameValuePair("username", settings.getUsername()),
new BasicNameValuePair("password", settings.getPassword()));
// The HttpClient will automatically remember the cookie for us, no need to parse it out.
@ -192,129 +196,126 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -192,129 +196,126 @@ public class QbittorrentAdapter implements IDaemonAdapter {
ensureAuthenticated(log);
switch (task.getMethod()) {
case Retrieve:
String path;
if (version >= 30200) {
path = "/query/torrents";
} else if (version >= 30000) {
path = "/json/torrents";
} else {
path = "/json/events";
}
case Retrieve:
String path;
if (version >= 30200) {
path = "/query/torrents";
} else if (version >= 30000) {
path = "/json/torrents";
} else {
path = "/json/events";
}
// Request all torrents from server
JSONArray result = new JSONArray(makeRequest(log, path));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
// Request all torrents from server
JSONArray result = new JSONArray(makeRequest(log, path));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
case GetTorrentDetails:
case GetTorrentDetails:
// Request tracker and error details for a specific teacher
String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages = new JSONArray(makeRequest(log,
(version >= 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/") + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(messages));
// Request tracker and error details for a specific teacher
String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages =
new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/") + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(messages));
case GetFileList:
case GetFileList:
// Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID();
JSONArray files = new JSONArray(makeRequest(log,
(version >= 30200 ? "/query/propertiesFiles/" : "/json/propertiesFiles/") + fhash));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files));
// Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID();
JSONArray files =
new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesFiles/" : "/json/propertiesFiles/") + fhash));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files));
case AddByFile:
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeUploadRequest("/command/upload", ufile, log);
return new DaemonTaskSuccessResult(task);
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeUploadRequest("/command/upload", ufile, log);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRequest(log, "/command/download", new BasicNameValuePair("urls", url));
return new DaemonTaskSuccessResult(task);
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRequest(log, "/command/download", new BasicNameValuePair("urls", url));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeRequest(log, "/command/download", new BasicNameValuePair("urls", magnet));
return new DaemonTaskSuccessResult(task);
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeRequest(log, "/command/download", new BasicNameValuePair("urls", magnet));
return new DaemonTaskSuccessResult(task);
case Remove:
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log, (removeTask.includingData() ? "/command/deletePerm" : "/command/delete"),
new BasicNameValuePair("hashes", removeTask.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log, (removeTask.includingData() ? "/command/deletePerm" : "/command/delete"),
new BasicNameValuePair("hashes", removeTask.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case Pause:
case Pause:
// Pause a torrent
makeRequest(log, "/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
// Pause a torrent
makeRequest(log, "/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case PauseAll:
case PauseAll:
// Resume all torrents
makeRequest(log, "/command/pauseall");
return new DaemonTaskSuccessResult(task);
// Resume all torrents
makeRequest(log, "/command/pauseall");
return new DaemonTaskSuccessResult(task);
case Resume:
case Resume:
// Resume a torrent
makeRequest(log, "/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
// Resume a torrent
makeRequest(log, "/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
case ResumeAll:
// Resume all torrents
makeRequest(log, "/command/resumeall");
return new DaemonTaskSuccessResult(task);
// Resume all torrents
makeRequest(log, "/command/resumeall");
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
case SetFilePriorities:
// Update the priorities to a set of files
SetFilePriorityTask setPrio = (SetFilePriorityTask) task;
String newPrio = "0";
if (setPrio.getNewPriority() == Priority.Low) {
newPrio = "1";
} else if (setPrio.getNewPriority() == Priority.Normal) {
newPrio = "2";
} else if (setPrio.getNewPriority() == Priority.High) {
newPrio = "7";
}
// We have to make a separate request per file, it seems
for (TorrentFile file : setPrio.getForFiles()) {
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent()
.getUniqueID()), new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair(
"priority", newPrio));
}
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// TODO: This doesn't seem to work yet
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
int dl = (ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate());
int ul = (ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate());
// First get the preferences
JSONObject prefs = new JSONObject(makeRequest(log, "/json/preferences"));
prefs.put("dl_limit", dl);
prefs.put("up_limit", ul);
makeRequest(log, "/command/setPreferences",
new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
// Update the priorities to a set of files
SetFilePriorityTask setPrio = (SetFilePriorityTask) task;
String newPrio = "0";
if (setPrio.getNewPriority() == Priority.Low) {
newPrio = "1";
} else if (setPrio.getNewPriority() == Priority.Normal) {
newPrio = "2";
} else if (setPrio.getNewPriority() == Priority.High) {
newPrio = "7";
}
// We have to make a separate request per file, it seems
for (TorrentFile file : setPrio.getForFiles()) {
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair("priority", newPrio));
}
return new DaemonTaskSuccessResult(task);
case SetTransferRates:
// TODO: This doesn't seem to work yet
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
int dl = (ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate());
int ul = (ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate());
// First get the preferences
JSONObject prefs = new JSONObject(makeRequest(log, "/json/preferences"));
prefs.put("dl_limit", dl);
prefs.put("up_limit", ul);
makeRequest(log, "/command/setPreferences", new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task,
new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -331,7 +332,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -331,7 +332,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
List<NameValuePair> nvps = new ArrayList<>();
Collections.addAll(nvps, params);
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
return makeWebRequest(httppost, log);
@ -349,7 +350,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -349,7 +350,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
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()));
return makeWebRequest(httppost, log);
@ -414,8 +415,8 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -414,8 +415,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private TorrentDetails parseJsonTorrentDetails(JSONArray messages) throws JSONException {
ArrayList<String> trackers = new ArrayList<String>();
ArrayList<String> errors = new ArrayList<String>();
ArrayList<String> trackers = new ArrayList<>();
ArrayList<String> errors = new ArrayList<>();
// Parse response
if (messages.length() > 0) {
@ -436,7 +437,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -436,7 +437,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private ArrayList<Torrent> parseJsonTorrents(JSONArray response) throws JSONException {
// Parse response
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
ArrayList<Torrent> torrents = new ArrayList<>();
for (int i = 0; i < response.length(); i++) {
JSONObject tor = response.getJSONObject(i);
int leechers[] = parsePeers(tor.getString("num_leechs"));
@ -533,10 +534,9 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -533,10 +534,9 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// In some situations it it just a "6" string
String[] parts = seeds.split(" ");
if (parts.length > 1) {
return new int[] { Integer.parseInt(parts[0]),
Integer.parseInt(parts[1].substring(1, parts[1].length() - 1)) };
return new int[]{Integer.parseInt(parts[0]), 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) {
@ -612,8 +612,8 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -612,8 +612,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
size = parseSize(file.getString("size"));
}
torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file
.getDouble("progress")), parsePriority(file.getInt("priority"))));
torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file.getDouble("progress")),
parsePriority(file.getInt("priority"))));
}
// Return the list

Loading…
Cancel
Save