|
|
@ -33,6 +33,8 @@ 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.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; |
|
|
@ -80,6 +82,7 @@ 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; |
|
|
@ -91,6 +94,22 @@ public class QbittorrentAdapter implements IDaemonAdapter { |
|
|
|
// We still need to retrieve the version number from the server
|
|
|
|
// 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
|
|
|
|
// 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)'
|
|
|
|
// 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); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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 about = makeRequest(log, "/about.html"); |
|
|
|
String aboutStartText = "qBittorrent v"; |
|
|
|
String aboutStartText = "qBittorrent v"; |
|
|
|
String aboutEndText = " (Web UI)"; |
|
|
|
String aboutEndText = " (Web UI)"; |
|
|
@ -127,6 +146,42 @@ public class QbittorrentAdapter implements IDaemonAdapter { |
|
|
|
} |
|
|
|
} |
|
|
|
// 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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
// If we don't have that cookie, let's try and get it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (apiVersion < 2) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Have we already authenticated? Check if we have the cookie that we need
|
|
|
|
|
|
|
|
List<Cookie> cookies = httpclient.getCookieStore().getCookies(); |
|
|
|
|
|
|
|
for (Cookie c : cookies) { |
|
|
|
|
|
|
|
if (c.getName().equals("SID")) { |
|
|
|
|
|
|
|
// And here it is! Okay, no need authenticate again.
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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...
|
|
|
|
|
|
|
|
cookies = httpclient.getCookieStore().getCookies(); |
|
|
|
|
|
|
|
for (Cookie c : cookies) { |
|
|
|
|
|
|
|
if (c.getName().equals("SID")) { |
|
|
|
|
|
|
|
// Good. Let's get out of here.
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// No cookie found, we didn't authenticate.
|
|
|
|
|
|
|
|
throw new DaemonException(ExceptionType.AuthenticationFailure, "Server rejected our login"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -134,18 +189,29 @@ public class QbittorrentAdapter implements IDaemonAdapter { |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
ensureVersion(log); |
|
|
|
ensureVersion(log); |
|
|
|
|
|
|
|
ensureAuthenticated(log); |
|
|
|
|
|
|
|
|
|
|
|
switch (task.getMethod()) { |
|
|
|
switch (task.getMethod()) { |
|
|
|
case Retrieve: |
|
|
|
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
|
|
|
|
// Request all torrents from server
|
|
|
|
JSONArray result = new JSONArray(makeRequest(log, version >= 30000 ? "/json/torrents" : "/json/events")); |
|
|
|
JSONArray result = new JSONArray(makeRequest(log, path)); |
|
|
|
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null); |
|
|
|
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null); |
|
|
|
|
|
|
|
|
|
|
|
case GetTorrentDetails: |
|
|
|
case GetTorrentDetails: |
|
|
|
|
|
|
|
|
|
|
|
// 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, "/json/propertiesTrackers/" + mhash)); |
|
|
|
JSONArray messages = 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)); |
|
|
|
|
|
|
|
|
|
|
@ -153,7 +219,8 @@ 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 = new JSONArray(makeRequest(log, "/json/propertiesFiles/" + fhash)); |
|
|
|
JSONArray files = 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: |
|
|
@ -374,10 +441,22 @@ public class QbittorrentAdapter implements IDaemonAdapter { |
|
|
|
JSONObject tor = response.getJSONObject(i); |
|
|
|
JSONObject tor = response.getJSONObject(i); |
|
|
|
int leechers[] = parsePeers(tor.getString("num_leechs")); |
|
|
|
int leechers[] = parsePeers(tor.getString("num_leechs")); |
|
|
|
int seeders[] = parsePeers(tor.getString("num_seeds")); |
|
|
|
int seeders[] = parsePeers(tor.getString("num_seeds")); |
|
|
|
long size = parseSize(tor.getString("size")); |
|
|
|
|
|
|
|
double ratio = parseRatio(tor.getString("ratio")); |
|
|
|
double ratio = parseRatio(tor.getString("ratio")); |
|
|
|
double progress = tor.getDouble("progress"); |
|
|
|
double progress = tor.getDouble("progress"); |
|
|
|
int dlspeed = parseSpeed(tor.getString("dlspeed")); |
|
|
|
long size; |
|
|
|
|
|
|
|
int dlspeed; |
|
|
|
|
|
|
|
int upspeed; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (apiVersion >= 2) { |
|
|
|
|
|
|
|
size = tor.getLong("size"); |
|
|
|
|
|
|
|
dlspeed = tor.getInt("dlspeed"); |
|
|
|
|
|
|
|
upspeed = tor.getInt("upspeed"); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
size = parseSize(tor.getString("size")); |
|
|
|
|
|
|
|
dlspeed = parseSpeed(tor.getString("dlspeed")); |
|
|
|
|
|
|
|
upspeed = parseSpeed(tor.getString("upspeed")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
long eta = -1L; |
|
|
|
long eta = -1L; |
|
|
|
if (dlspeed > 0) |
|
|
|
if (dlspeed > 0) |
|
|
|
eta = (long) (size - (size * progress)) / dlspeed; |
|
|
|
eta = (long) (size - (size * progress)) / dlspeed; |
|
|
@ -391,7 +470,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { |
|
|
|
parseStatus(tor.getString("state")), |
|
|
|
parseStatus(tor.getString("state")), |
|
|
|
null, |
|
|
|
null, |
|
|
|
dlspeed, |
|
|
|
dlspeed, |
|
|
|
parseSpeed(tor.getString("upspeed")), |
|
|
|
upspeed, |
|
|
|
seeders[0], |
|
|
|
seeders[0], |
|
|
|
seeders[1], |
|
|
|
seeders[1], |
|
|
|
leechers[0], |
|
|
|
leechers[0], |
|
|
@ -525,7 +604,14 @@ public class QbittorrentAdapter implements IDaemonAdapter { |
|
|
|
ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>(); |
|
|
|
ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>(); |
|
|
|
for (int i = 0; i < response.length(); i++) { |
|
|
|
for (int i = 0; i < response.length(); i++) { |
|
|
|
JSONObject file = response.getJSONObject(i); |
|
|
|
JSONObject file = response.getJSONObject(i); |
|
|
|
long size = parseSize(file.getString("size")); |
|
|
|
|
|
|
|
|
|
|
|
long size; |
|
|
|
|
|
|
|
if (apiVersion >= 2) { |
|
|
|
|
|
|
|
size = file.getLong("size"); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
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")), parsePriority(file.getInt("priority")))); |
|
|
|
.getDouble("progress")), parsePriority(file.getInt("priority")))); |
|
|
|
} |
|
|
|
} |
|
|
|