Browse Source

More qBittorrent API version change fixes cf #516

pull/527/head
Eric Kok 5 years ago
parent
commit
b80430a6ff
  1. 197
      app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java

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

@ -45,7 +45,26 @@ 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.task.*; import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import org.transdroid.daemon.task.GetFileListTask;
import org.transdroid.daemon.task.GetFileListTaskSuccessResult;
import org.transdroid.daemon.task.GetStatsTask;
import org.transdroid.daemon.task.GetStatsTaskSuccessResult;
import org.transdroid.daemon.task.GetTorrentDetailsTask;
import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult;
import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetDownloadLocationTask;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.HttpHelper; import org.transdroid.daemon.util.HttpHelper;
import java.io.File; import java.io.File;
@ -61,6 +80,7 @@ import java.util.Map;
/** /**
* The daemon adapter for the qBittorrent torrent client. * The daemon adapter for the qBittorrent torrent client.
*
* @author erickok * @author erickok
*/ */
public class QbittorrentAdapter implements IDaemonAdapter { public class QbittorrentAdapter implements IDaemonAdapter {
@ -70,71 +90,28 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private DaemonSettings settings; private DaemonSettings settings;
private DefaultHttpClient httpclient; private DefaultHttpClient httpclient;
private int version = -1; private int version = -1;
private int apiVersion = -1;
public QbittorrentAdapter(DaemonSettings settings) { public QbittorrentAdapter(DaemonSettings settings) {
this.settings = settings; this.settings = settings;
} }
private synchronized void ensureVersion(Log log) throws DaemonException { private synchronized void ensureVersion(Log log) {
// Still need to retrieve the API and qBittorrent version numbers from the server? // Still need to retrieve the API and qBittorrent version numbers from the server?
if (version > 0 && apiVersion > 0) if (version > 0)
return; return;
// Since 4.1, API v2 is used. Since qBittorrent 3.2, API v1 is used. Otherwise we use unofficial legacy json endpoints.
try { try {
// Since 4.2.0, old API is dropped. Fallback to old one if the new one failed for version <4.2.0
// The API version is only supported since qBittorrent 3.2, so otherwise we assume version 1
boolean is_v2 = false;
String apiVersionText = "";
// First, try the v2 api version endpoint
try {
apiVersionText = makeRequest(log, "/api/v2/app/webapiVersion");
} catch (DaemonException e) {
// 403 Forbidden - endpoint exists. Keep trying v2
is_v2 = e.getType() == ExceptionType.AuthenticationFailure;
}
// Keep trying
if (apiVersion < 0) {
if (is_v2) {
// Preemptive assumption, for authentication
apiVersion = 20300; //2.3.0
// Authenticate, and try v2 again
try {
ensureAuthenticated(log);
apiVersionText = makeRequest(log, "/api/v2/app/webapiVersion");
} catch (DaemonException e) {
apiVersion = 20300; // assume this is new API 2.3.0 since we are forbidden to access API
}
} else {
// Fall back to old api
try {
apiVersionText = makeRequest(log, "/version/api");
} catch (DaemonException e) {
apiVersion = 1;
}
}
}
if (apiVersion < 0) {
apiVersion = parseVersionNumber(apiVersionText);
}
// The qBittorent version is only supported since 3.2; for earlier versions we parse the about dialog and parse it
// Since 4.2.0, new API version is used instead
String versionText = ""; String versionText = "";
if (apiVersion >= 20300) { try {
ensureAuthenticated(log); // Try v2 API first, which returns version number in 'v4.1.9' format
versionText = makeRequest(log, "/api/v2/app/version").substring(1); versionText = makeRequest(log, "/api/v2/app/version").substring(1);
} else if (apiVersion > 10000) { } catch (Exception e1) {
// Format is something like 'v3.2.0' // Try v1 API, which returns version number in 'v3.2.0' format
try {
versionText = makeRequest(log, "/version/qbittorrent").substring(1); versionText = makeRequest(log, "/version/qbittorrent").substring(1);
} else { } catch (Exception e2) {
// Format is something like 'qBittorrent v2.9.7 (Web UI)' or 'qBittorrent v3.0.0-alpha5 (Web UI)' // Legacy mode; 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)";
@ -144,13 +121,13 @@ public class QbittorrentAdapter implements IDaemonAdapter {
versionText = about.substring(aboutStart + aboutStartText.length(), aboutEnd); versionText = about.substring(aboutStart + aboutStartText.length(), aboutEnd);
} }
} }
}
version = parseVersionNumber(versionText); version = parseVersionNumber(versionText);
} catch (Exception 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 = 10000;
} }
} }
@ -162,7 +139,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
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) {
version += Float.parseFloat(parts[1]) * 100; version += Integer.parseInt(parts[1]) * 100;
if (parts.length > 2) { if (parts.length > 2) {
// For the last part only read until a non-numeric character is read // 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 // For example version 3.0.0-alpha5 is read as version code 30000
@ -176,7 +153,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
break; break;
} }
} }
version += Float.parseFloat(numbers); version += Integer.parseInt(numbers);
} }
} }
} }
@ -186,55 +163,60 @@ public class QbittorrentAdapter implements IDaemonAdapter {
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.
if (version != -1 && version < 30200) {
if (apiVersion < 20000) {
return; return;
} }
// Have we already authenticated? Check if we have the cookie that we need // Have we already authenticated? Check if we have the cookie that we need
List<Cookie> cookies = httpclient.getCookieStore().getCookies(); if (isAuthenticated()) {
for (Cookie c : cookies) {
if (c.getName().equals("SID")) {
// And here it is! Okay, no need authenticate again.
return; return;
} }
final BasicNameValuePair usernameParam = new BasicNameValuePair("username", settings.getUsername());
final BasicNameValuePair passwordParam = new BasicNameValuePair("password", settings.getPassword());
if (version == -1 || version >= 40100) {
try {
makeRequest(log, "/api/v2/auth/login", usernameParam, passwordParam);
} catch (DaemonException ignored) {
}
}
if (!isAuthenticated()) {
try {
makeRequest(log, "/login", usernameParam, passwordParam);
} catch (DaemonException ignored) {
}
} }
if (apiVersion >= 20300) { if (!isAuthenticated()) {
makeRequest(log, "/api/v2/auth/login", new BasicNameValuePair("username", settings.getUsername()), throw new DaemonException(ExceptionType.AuthenticationFailure, "Server rejected our login");
new BasicNameValuePair("password", settings.getPassword())); }
} else {
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.
// However, we would like to see if authentication was successful or not... private boolean isAuthenticated() {
cookies = httpclient.getCookieStore().getCookies(); List<Cookie> cookies = httpclient.getCookieStore().getCookies();
for (Cookie c : cookies) { for (Cookie c : cookies) {
if (c.getName().equals("SID")) { if (c.getName().equals("SID")) {
// Good. Let's get out of here. // And here it is! Okay, no need authenticate again.
return; return true;
} }
} }
return false;
// No cookie found, we didn't authenticate.
throw new DaemonException(ExceptionType.AuthenticationFailure, "Server rejected our login");
} }
@Override @Override
public DaemonTaskResult executeTask(Log log, DaemonTask task) { public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try { try {
ensureVersion(log); initialise();
ensureAuthenticated(log); ensureAuthenticated(log);
ensureVersion(log);
switch (task.getMethod()) { switch (task.getMethod()) {
case Retrieve: case Retrieve:
// Request all torrents from server // Request all torrents from server
String path; String path;
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/info"; path = "/api/v2/torrents/info";
} else if (version >= 30200) { } else if (version >= 30200) {
path = "/query/torrents"; path = "/query/torrents";
@ -254,7 +236,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
String mhash = task.getTargetTorrent().getUniqueID(); String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages; JSONArray messages;
JSONArray pieces; JSONArray pieces;
if (version >= 40200) { if (version >= 40100) {
messages = new JSONArray(makeRequest(log, "/api/v2/torrents/trackers", new BasicNameValuePair("hash", mhash))); messages = new JSONArray(makeRequest(log, "/api/v2/torrents/trackers", new BasicNameValuePair("hash", mhash)));
pieces = new JSONArray(makeRequest(log, "/api/v2/torrents/pieceStates", new BasicNameValuePair("hash", mhash))); pieces = new JSONArray(makeRequest(log, "/api/v2/torrents/pieceStates", new BasicNameValuePair("hash", mhash)));
} else { } else {
@ -269,7 +251,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// 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; JSONArray files;
if (version >= 40200) { if (version >= 40100) {
files = new JSONArray(makeRequest(log, "/api/v2/torrents/files", new BasicNameValuePair("hash", fhash))); files = new JSONArray(makeRequest(log, "/api/v2/torrents/files", new BasicNameValuePair("hash", fhash)));
} else if (version >= 30200) { } else if (version >= 30200) {
files = new JSONArray(makeRequest(log, "/query/propertiesFiles/" + fhash)); files = new JSONArray(makeRequest(log, "/query/propertiesFiles/" + fhash));
@ -282,7 +264,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case AddByFile: case AddByFile:
// Upload a local .torrent file // Upload a local .torrent file
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/add"; path = "/api/v2/torrents/add";
} else { } else {
path = "/command/upload"; path = "/command/upload";
@ -296,7 +278,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Request to add a torrent by URL // Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl(); String url = ((AddByUrlTask) task).getUrl();
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/add"; path = "/api/v2/torrents/add";
} else { } else {
path = "/command/upload"; path = "/command/upload";
@ -309,7 +291,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Request to add a magnet link by URL // Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl(); String magnet = ((AddByMagnetUrlTask) task).getUrl();
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/add"; path = "/api/v2/torrents/add";
} else { } else {
path = "/command/download"; path = "/command/download";
@ -322,7 +304,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Remove a torrent // Remove a torrent
RemoveTask removeTask = (RemoveTask) task; RemoveTask removeTask = (RemoveTask) task;
if (version >= 40200) { if (version >= 40100) {
if (removeTask.includingData()) { if (removeTask.includingData()) {
makeRequest(log, "/api/v2/torrents/delete", makeRequest(log, "/api/v2/torrents/delete",
new BasicNameValuePair("hashes", removeTask.getTargetTorrent().getUniqueID()), new BasicNameValuePair("hashes", removeTask.getTargetTorrent().getUniqueID()),
@ -343,7 +325,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case Pause: case Pause:
// Pause a torrent // Pause a torrent
if (version >= 40200) { if (version >= 40100) {
makeRequest(log, "/api/v2/torrents/pause", new BasicNameValuePair("hashes", task.getTargetTorrent().getUniqueID())); makeRequest(log, "/api/v2/torrents/pause", new BasicNameValuePair("hashes", task.getTargetTorrent().getUniqueID()));
} else { } else {
makeRequest(log, "/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID())); makeRequest(log, "/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
@ -354,7 +336,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case PauseAll: case PauseAll:
// Resume all torrents // Resume all torrents
if (version >= 40200) { if (version >= 40100) {
makeRequest(log, "/api/v2/torrents/pause", new BasicNameValuePair("hashes", "all")); makeRequest(log, "/api/v2/torrents/pause", new BasicNameValuePair("hashes", "all"));
} else { } else {
makeRequest(log, "/command/pauseall"); makeRequest(log, "/command/pauseall");
@ -365,7 +347,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case Resume: case Resume:
// Resume a torrent // Resume a torrent
if (version >= 40200) { if (version >= 40100) {
makeRequest(log, "/api/v2/torrents/resume", new BasicNameValuePair("hashes", task.getTargetTorrent().getUniqueID())); makeRequest(log, "/api/v2/torrents/resume", new BasicNameValuePair("hashes", task.getTargetTorrent().getUniqueID()));
} else { } else {
makeRequest(log, "/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID())); makeRequest(log, "/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
@ -376,7 +358,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case ResumeAll: case ResumeAll:
// Resume all torrents // Resume all torrents
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/resume"; path = "/api/v2/torrents/resume";
makeRequest(log, path, new BasicNameValuePair("hashes", "all")); makeRequest(log, path, new BasicNameValuePair("hashes", "all"));
} else { } else {
@ -399,7 +381,7 @@ 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()) {
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/filePrio"; path = "/api/v2/torrents/filePrio";
} else { } else {
path = "/command/setFilePrio"; path = "/command/setFilePrio";
@ -413,7 +395,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case ForceRecheck: case ForceRecheck:
// Force recheck a torrent // Force recheck a torrent
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/recheck"; path = "/api/v2/torrents/recheck";
} else { } else {
path = "/command/recheck"; path = "/command/recheck";
@ -424,7 +406,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case ToggleSequentialDownload: case ToggleSequentialDownload:
// Toggle sequential download mode on a torrent // Toggle sequential download mode on a torrent
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/toggleSequentialDownload"; path = "/api/v2/torrents/toggleSequentialDownload";
} else { } else {
path = "/command/toggleSequentialDownload"; path = "/command/toggleSequentialDownload";
@ -435,7 +417,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case ToggleFirstLastPieceDownload: case ToggleFirstLastPieceDownload:
// Set policy for downloading first and last piece first on a torrent // Set policy for downloading first and last piece first on a torrent
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/toggleFirstLastPiecePrio"; path = "/api/v2/torrents/toggleFirstLastPiecePrio";
} else { } else {
path = "/command/toggleFirstLastPiecePrio"; path = "/command/toggleFirstLastPiecePrio";
@ -446,7 +428,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case SetLabel: case SetLabel:
SetLabelTask labelTask = (SetLabelTask) task; SetLabelTask labelTask = (SetLabelTask) task;
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/setCategory"; path = "/api/v2/torrents/setCategory";
} else { } else {
path = "/command/setCategory"; path = "/command/setCategory";
@ -459,7 +441,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case SetDownloadLocation: case SetDownloadLocation:
SetDownloadLocationTask setLocationTask = (SetDownloadLocationTask) task; SetDownloadLocationTask setLocationTask = (SetDownloadLocationTask) task;
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/torrents/setLocation"; path = "/api/v2/torrents/setLocation";
} else { } else {
path = "/command/setLocation"; path = "/command/setLocation";
@ -478,7 +460,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
String dl = (ratesTask.getDownloadRate() == null ? "NaN" : Long.toString(ratesTask.getDownloadRate() * 1024)); String dl = (ratesTask.getDownloadRate() == null ? "NaN" : Long.toString(ratesTask.getDownloadRate() * 1024));
String ul = (ratesTask.getUploadRate() == null ? "NaN" : Long.toString(ratesTask.getUploadRate() * 1024)); String ul = (ratesTask.getUploadRate() == null ? "NaN" : Long.toString(ratesTask.getUploadRate() * 1024));
if (version >= 40200) { if (version >= 40100) {
pathDL = "/api/v2/torrents/setDownloadLimit"; pathDL = "/api/v2/torrents/setDownloadLimit";
pathUL = "/api/v2/torrents/setUploadLimit"; pathUL = "/api/v2/torrents/setUploadLimit";
} else { } else {
@ -493,7 +475,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case GetStats: case GetStats:
// Refresh alternative download speeds setting // Refresh alternative download speeds setting
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/sync/maindata?rid=0"; path = "/api/v2/sync/maindata?rid=0";
} else { } else {
path = "/sync/maindata?rid=0"; path = "/sync/maindata?rid=0";
@ -509,7 +491,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case SetAlternativeMode: case SetAlternativeMode:
// Flip alternative speed mode // Flip alternative speed mode
if (version >= 40200) { if (version >= 40100) {
path = "/api/v2/transfer/toggleSpeedLimitsMode"; path = "/api/v2/transfer/toggleSpeedLimitsMode";
} else { } else {
path = "/command/toggleAlternativeSpeedLimits"; path = "/command/toggleAlternativeSpeedLimits";
@ -567,12 +549,6 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private String makeWebRequest(HttpPost httppost, Log log) throws DaemonException { private String makeWebRequest(HttpPost httppost, Log log) throws DaemonException {
try { try {
// Initialise the HTTP client
if (httpclient == null) {
initialise();
}
// Execute // Execute
HttpResponse response = httpclient.execute(httppost); HttpResponse response = httpclient.execute(httppost);
@ -604,8 +580,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
if (e instanceof DaemonException) { if (e instanceof DaemonException) {
throw (DaemonException) e; throw (DaemonException) e;
} } else {
else {
throw new DaemonException(ExceptionType.ConnectionError, e.toString()); throw new DaemonException(ExceptionType.ConnectionError, e.toString());
} }
} }
@ -614,14 +589,18 @@ public class QbittorrentAdapter implements IDaemonAdapter {
/** /**
* Instantiates an HTTP client with proper credentials that can be used for all qBittorrent requests. * Instantiates an HTTP client with proper credentials that can be used for all qBittorrent requests.
*
* @throws DaemonException On conflicting or missing settings * @throws DaemonException On conflicting or missing settings
*/ */
private void initialise() throws DaemonException { private void initialise() throws DaemonException {
if (httpclient == null) {
httpclient = HttpHelper.createStandardHttpClient(settings, true); httpclient = HttpHelper.createStandardHttpClient(settings, true);
} }
}
/** /**
* Build the URL of the web UI request from the user settings * Build the URL of the web UI request from the user settings
*
* @return The URL to request * @return The URL to request
*/ */
private String buildWebUIUrl(String path) { private String buildWebUIUrl(String path) {
@ -667,7 +646,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
Map<String, Label> labels = new HashMap<>(); Map<String, Label> labels = new HashMap<>();
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);
if (apiVersion >= 20000) { if (version >= 40100) {
String label = tor.optString("category"); String label = tor.optString("category");
if (label != null && label.length() > 0) { if (label != null && label.length() > 0) {
final Label labelObject = labels.get(label); final Label labelObject = labels.get(label);
@ -699,7 +678,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
Date completionOn = null; Date completionOn = null;
String label = null; String label = null;
if (apiVersion >= 20000) { if (version >= 30200) {
leechers = new int[2]; leechers = new int[2];
leechers[0] = tor.getInt("num_leechs"); leechers[0] = tor.getInt("num_leechs");
leechers[1] = tor.getInt("num_complete") + tor.getInt("num_incomplete"); leechers[1] = tor.getInt("num_complete") + tor.getInt("num_incomplete");
@ -891,7 +870,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
JSONObject file = response.getJSONObject(i); JSONObject file = response.getJSONObject(i);
long size; long size;
if (apiVersion >= 20000) { if (version >= 30200) {
size = file.getLong("size"); size = file.getLong("size");
} else { } else {
size = parseSize(file.getString("size")); size = parseSize(file.getString("size"));

Loading…
Cancel
Save