@ -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,36 +89,40 @@ public class QbittorrentAdapter implements IDaemonAdapter {
@@ -89,36 +89,40 @@ 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 {
// 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 e ) {
} catch ( DaemonException | NumberFormatException 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
// 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 ) ;
try {
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 = about . substring ( aboutStart + aboutStartText . length ( ) , aboutEnd ) . split ( "\\." ) ;
String [ ] parts = versionText . split ( "\\." ) ;
if ( parts . length > 0 ) {
version = Integer . parseInt ( parts [ 0 ] ) * 100 * 100 ;
if ( parts . length > 1 ) {
@ -141,14 +145,15 @@ public class QbittorrentAdapter implements IDaemonAdapter {
@@ -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
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.
@ -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.
@ -210,17 +214,16 @@ public class QbittorrentAdapter implements IDaemonAdapter {
@@ -210,17 +214,16 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// 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 ) ) ;
JSONArray messages =
new JSONArray ( makeRequest ( log , ( version > = 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/" ) + mhash ) ) ;
return new GetTorrentDetailsTaskSuccessResult ( ( GetTorrentDetailsTask ) task , parseJsonTorrentDetails ( messages ) ) ;
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 ) ) ;
JSONArray files =
new JSONArray ( makeRequest ( log , ( version > = 30200 ? "/query/propertiesFiles/" : "/json/propertiesFiles/" ) + fhash ) ) ;
return new GetFileListTaskSuccessResult ( ( GetFileListTask ) task , parseJsonFiles ( files ) ) ;
case AddByFile :
@ -290,9 +293,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
@@ -290,9 +293,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
// 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 ) ) ;
makeRequest ( log , "/command/setFilePrio" , new BasicNameValuePair ( "hash" , task . getTargetTorrent ( ) . getUniqueID ( ) ) ,
new BasicNameValuePair ( "id" , file . getKey ( ) ) , new BasicNameValuePair ( "priority" , newPrio ) ) ;
}
return new DaemonTaskSuccessResult ( task ) ;
@ -308,13 +310,12 @@ public class QbittorrentAdapter implements IDaemonAdapter {
@@ -308,13 +310,12 @@ public class QbittorrentAdapter implements IDaemonAdapter {
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 ) ) ) ;
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 ( ) ) ) ;
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 ) ;
@ -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,8 +534,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
@@ -533,8 +534,7 @@ 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 ] ) } ;
}
@ -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