@ -70,9 +70,7 @@ import com.android.internalcopy.http.multipart.Part;
/ * *
/ * *
* 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 {
@ -139,41 +137,42 @@ public class QbittorrentAdapter implements IDaemonAdapter {
case Retrieve :
case Retrieve :
// Request all torrents from server
// Request all torrents from server
JSONArray result = new JSONArray ( makeRequest ( version > = 30000 ? "/json/torrents" : "/json/events" ) ) ;
JSONArray result = new JSONArray ( makeRequest ( version > = 30000 ? "/json/torrents" : "/json/events" ) ) ;
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 = ( ( GetTorrentDetailsTask ) task ) . getTargetTorrent ( ) . getUniqueID ( ) ;
String mhash = ( ( GetTorrentDetailsTask ) task ) . getTargetTorrent ( ) . getUniqueID ( ) ;
JSONArray messages = new JSONArray ( makeRequest ( "/json/propertiesTrackers/" + mhash ) ) ;
JSONArray messages = new JSONArray ( makeRequest ( "/json/propertiesTrackers/" + mhash ) ) ;
return new GetTorrentDetailsTaskSuccessResult ( ( GetTorrentDetailsTask ) task , parseJsonTorrentDetails ( messages ) ) ;
return new GetTorrentDetailsTaskSuccessResult ( ( GetTorrentDetailsTask ) task ,
parseJsonTorrentDetails ( messages ) ) ;
case GetFileList :
case GetFileList :
// Request files listing for a specific torrent
// Request files listing for a specific torrent
String fhash = ( ( GetFileListTask ) task ) . getTargetTorrent ( ) . getUniqueID ( ) ;
String fhash = ( ( GetFileListTask ) task ) . getTargetTorrent ( ) . getUniqueID ( ) ;
JSONArray files = new JSONArray ( makeRequest ( "/json/propertiesFiles/" + fhash ) ) ;
JSONArray files = new JSONArray ( makeRequest ( "/json/propertiesFiles/" + fhash ) ) ;
return new GetFileListTaskSuccessResult ( ( GetFileListTask ) task , parseJsonFiles ( files ) ) ;
return new GetFileListTaskSuccessResult ( ( GetFileListTask ) task , parseJsonFiles ( files ) ) ;
case AddByFile :
case AddByFile :
// Upload a local .torrent file
// Upload a local .torrent file
String ufile = ( ( AddByFileTask ) task ) . getFile ( ) ;
String ufile = ( ( AddByFileTask ) task ) . getFile ( ) ;
makeUploadRequest ( "/command/upload" , ufile ) ;
makeUploadRequest ( "/command/upload" , ufile ) ;
return new DaemonTaskSuccessResult ( task ) ;
return new DaemonTaskSuccessResult ( task ) ;
case AddByUrl :
case AddByUrl :
// Request to add a torrent by URL
// Request to add a torrent by URL
String url = ( ( AddByUrlTask ) task ) . getUrl ( ) ;
String url = ( ( AddByUrlTask ) task ) . getUrl ( ) ;
makeRequest ( "/command/download" , new BasicNameValuePair ( "urls" , url ) ) ;
makeRequest ( "/command/download" , new BasicNameValuePair ( "urls" , url ) ) ;
return new DaemonTaskSuccessResult ( task ) ;
return new DaemonTaskSuccessResult ( task ) ;
case AddByMagnetUrl :
case AddByMagnetUrl :
// 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 ( ) ;
makeRequest ( "/command/download" , new BasicNameValuePair ( "urls" , magnet ) ) ;
makeRequest ( "/command/download" , new BasicNameValuePair ( "urls" , magnet ) ) ;
return new DaemonTaskSuccessResult ( task ) ;
return new DaemonTaskSuccessResult ( task ) ;
@ -181,7 +180,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Remove a torrent
// Remove a torrent
RemoveTask removeTask = ( RemoveTask ) task ;
RemoveTask removeTask = ( RemoveTask ) task ;
makeRequest ( ( removeTask . includingData ( ) ? "/command/deletePerm" : "/command/delete" ) , new BasicNameValuePair ( "hashes" , removeTask . getTargetTorrent ( ) . getUniqueID ( ) ) ) ;
makeRequest ( ( removeTask . includingData ( ) ? "/command/deletePerm" : "/command/delete" ) ,
new BasicNameValuePair ( "hashes" , removeTask . getTargetTorrent ( ) . getUniqueID ( ) ) ) ;
return new DaemonTaskSuccessResult ( task ) ;
return new DaemonTaskSuccessResult ( task ) ;
case Pause :
case Pause :
@ -222,7 +222,9 @@ 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 ( "/command/setFilePrio" , new BasicNameValuePair ( "hash" , task . getTargetTorrent ( ) . getUniqueID ( ) ) , new BasicNameValuePair ( "id" , file . getKey ( ) ) , new BasicNameValuePair ( "priority" , newPrio ) ) ;
makeRequest ( "/command/setFilePrio" , new BasicNameValuePair ( "hash" , task . getTargetTorrent ( )
. getUniqueID ( ) ) , new BasicNameValuePair ( "id" , file . getKey ( ) ) , new BasicNameValuePair (
"priority" , newPrio ) ) ;
}
}
return new DaemonTaskSuccessResult ( task ) ;
return new DaemonTaskSuccessResult ( task ) ;
@ -231,18 +233,20 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// TODO: This doesn't seem to work yet
// TODO: This doesn't seem to work yet
// Request to set the maximum transfer rates
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = ( SetTransferRatesTask ) task ;
SetTransferRatesTask ratesTask = ( SetTransferRatesTask ) task ;
int dl = ( ratesTask . getDownloadRate ( ) = = null ? - 1 : ratesTask . getDownloadRate ( ) . intValue ( ) ) ;
int dl = ( ratesTask . getDownloadRate ( ) = = null ? - 1 : ratesTask . getDownloadRate ( ) . intValue ( ) ) ;
int ul = ( ratesTask . getUploadRate ( ) = = null ? - 1 : ratesTask . getUploadRate ( ) . intValue ( ) ) ;
int ul = ( ratesTask . getUploadRate ( ) = = null ? - 1 : ratesTask . getUploadRate ( ) . intValue ( ) ) ;
// First get the preferences
// First get the preferences
JSONObject prefs = new JSONObject ( makeRequest ( "/json/preferences" ) ) ;
JSONObject prefs = new JSONObject ( makeRequest ( "/json/preferences" ) ) ;
prefs . put ( "dl_limit" , dl ) ;
prefs . put ( "dl_limit" , dl ) ;
prefs . put ( "up_limit" , ul ) ;
prefs . put ( "up_limit" , ul ) ;
makeRequest ( "/command/setPreferences" , new BasicNameValuePair ( "json" , URLEncoder . encode ( prefs . toString ( ) , HTTP . UTF_8 ) ) ) ;
makeRequest ( "/command/setPreferences" ,
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 , 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 ) {
} catch ( JSONException e ) {
return new DaemonTaskFailureResult ( task , new DaemonException ( ExceptionType . ParsingFailed , e . toString ( ) ) ) ;
return new DaemonTaskFailureResult ( task , new DaemonException ( ExceptionType . ParsingFailed , e . toString ( ) ) ) ;
@ -309,7 +313,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
String result = HttpHelper . convertStreamToString ( instream ) ;
String result = HttpHelper . convertStreamToString ( instream ) ;
instream . close ( ) ;
instream . close ( ) ;
//TLog.d(LOG_NAME, "Success: " + (result.length() > 300? result.substring(0, 300) + "... (" + result.length() + " chars)": result));
// TLog.d(LOG_NAME, "Success: " + (result.length() > 300? result.substring(0, 300) + "... (" +
// result.length() + " chars)": result));
// Return raw result
// Return raw result
return result ;
return result ;
@ -331,18 +336,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
* @throws DaemonException On conflicting or missing settings
* @throws DaemonException On conflicting or missing settings
* /
* /
private void initialise ( ) throws DaemonException {
private void initialise ( ) throws DaemonException {
httpclient = HttpHelper . createStandardHttpClient ( settings , true ) ;
httpclient = HttpHelper . createStandardHttpClient ( settings , true ) ;
/ * httpclient . addRequestInterceptor ( new HttpRequestInterceptor ( ) {
@Override
public void process ( HttpRequest request , HttpContext context ) throws HttpException , IOException {
for ( Header header : request . getAllHeaders ( ) ) {
TLog . d ( LOG_NAME , "Request: " + header . getName ( ) + ": " + header . getValue ( ) ) ;
}
}
} ) ; * /
}
}
/ * *
/ * *
@ -387,30 +381,15 @@ public class QbittorrentAdapter implements IDaemonAdapter {
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" ) ) ;
int dlspeed = parseSpeed ( tor . getString ( "dlspeed" ) ) ;
long eta = - 1L ;
if ( dlspeed > 0 )
eta = ( long ) ( size - ( size * progress ) ) / dlspeed ;
// Date added is only available in /json/propertiesGeneral on a per-torrent basis, unfortunately
// Add the parsed torrent to the list
// Add the parsed torrent to the list
torrents . add ( new Torrent (
torrents . add ( new Torrent ( ( long ) i , tor . getString ( "hash" ) , tor . getString ( "name" ) , parseStatus ( tor
( long ) i ,
. getString ( "state" ) ) , null , dlspeed , parseSpeed ( tor . getString ( "upspeed" ) ) , leechers , leechers
tor . getString ( "hash" ) ,
+ seeders , known , known , ( int ) eta , ( long ) ( size * progress ) , ( long ) ( size * ratio ) , size ,
tor . getString ( "name" ) ,
( float ) progress , 0f , null , null , null , null , settings . getType ( ) ) ) ;
parseStatus ( tor . getString ( "state" ) ) ,
null ,
dlspeed ,
parseSpeed ( tor . getString ( "upspeed" ) ) ,
leechers ,
leechers + seeders ,
known ,
known ,
( int ) ( ( size - ( size * progress ) ) / dlspeed ) ,
( long ) ( size * progress ) ,
( long ) ( size * ratio ) ,
size ,
( float ) progress ,
0f ,
null ,
null , // Only available in /json/propertiesGeneral on a per-torrent basis, unfortunately
null ,
null ,
settings . getType ( ) ) ) ;
}
}
// Return the list
// Return the list
@ -421,6 +400,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private double parseRatio ( String string ) {
private double parseRatio ( String string ) {
// Ratio is given in "1.5" string format
// Ratio is given in "1.5" string format
try {
try {
// FIXME Hack for issue #115: Strip the possible . and , separators in a hopefully reliable fashion, for now
string = string . replace ( "," , "." ) ;
return Double . parseDouble ( string ) ;
return Double . parseDouble ( string ) ;
} catch ( Exception e ) {
} catch ( Exception e ) {
return 0D ;
return 0D ;
@ -431,19 +412,31 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// See https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-Documentation
// See https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-Documentation
if ( string . equals ( "Unknown" ) )
if ( string . equals ( "Unknown" ) )
return - 1 ;
return - 1 ;
// Sizes are given in "70 3.3 MiB"-like string format
// Sizes are given in "1,02 3.3 MiB"-like string format
// Returns size in B-based long
// FIXME Hack for issue #115: Strip the possible . and , separators in a hopefully reliable fashion, for now
String [ ] parts = string . split ( " " ) ;
String [ ] parts = string . split ( " " ) ;
String part1 = "" ;
if ( parts [ 0 ] . length ( ) > = 3 )
part1 = parts [ 0 ] . substring ( 0 , parts [ 0 ] . length ( ) - 3 ) ;
String part2 = parts [ 0 ] . substring ( parts [ 0 ] . length ( ) - 3 ) ;
parts [ 0 ] = part1 . replace ( "Ê" , "" ) . replace ( " " , "" ) . replace ( "," , "" ) . replace ( "." , "" ) + part2 . replace ( "," , "." ) ;
// Returns size in B-based long
double number ;
try {
number = Double . parseDouble ( parts [ 0 ] ) ;
} catch ( Exception e ) {
return - 1L ;
}
if ( parts [ 1 ] . equals ( "TiB" ) ) {
if ( parts [ 1 ] . equals ( "TiB" ) ) {
return ( long ) ( Double . parseDouble ( parts [ 0 ] ) * 1024L * 1024L * 1024L * 1024L ) ;
return ( long ) ( n um ber * 1024L * 1024L * 1024L * 1024L ) ;
} else if ( parts [ 1 ] . equals ( "GiB" ) ) {
} else if ( parts [ 1 ] . equals ( "GiB" ) ) {
return ( long ) ( Double . parseDouble ( parts [ 0 ] ) * 1024L * 1024L * 1024L ) ;
return ( long ) ( n um ber * 1024L * 1024L * 1024L ) ;
} else if ( parts [ 1 ] . equals ( "MiB" ) ) {
} else if ( parts [ 1 ] . equals ( "MiB" ) ) {
return ( long ) ( Double . parseDouble ( parts [ 0 ] ) * 1024L * 1024L ) ;
return ( long ) ( n um ber * 1024L * 1024L ) ;
} else if ( parts [ 1 ] . equals ( "KiB" ) ) {
} else if ( parts [ 1 ] . equals ( "KiB" ) ) {
return ( long ) ( Double . parseDouble ( parts [ 0 ] ) * 1024L ) ;
return ( long ) ( n um ber * 1024L ) ;
}
}
return ( long ) ( Double . parseDouble ( parts [ 0 ] ) ) ;
return ( long ) n um ber;
}
}
private int parseKnown ( String leechs , String seeds ) {
private int parseKnown ( String leechs , String seeds ) {
@ -533,14 +526,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
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 = parseSize ( file . getString ( "size" ) ) ;
torrentfiles . add ( new TorrentFile (
torrentfiles . add ( new TorrentFile ( "" + i , file . getString ( "name" ) , null , null , size , ( long ) ( size * file
"" + i ,
. getDouble ( "progress" ) ) , parsePriority ( file . getInt ( "priority" ) ) ) ) ;
file . getString ( "name" ) ,
null ,
null ,
size ,
( long ) ( size * file . getDouble ( "progress" ) ) ,
parsePriority ( file . getInt ( "priority" ) ) ) ) ;
}
}
// Return the list
// Return the list