You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
677 lines
30 KiB
677 lines
30 KiB
/* |
|
* This file is part of Transdroid <http://www.transdroid.org> |
|
* |
|
* Transdroid is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* Transdroid is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
package org.transdroid.daemon.adapters.rTorrent; |
|
|
|
import android.text.TextUtils; |
|
import de.timroes.axmlrpc.XMLRPCClient; |
|
import de.timroes.axmlrpc.XMLRPCClient.UnauthorizdException; |
|
import de.timroes.axmlrpc.XMLRPCException; |
|
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.Label; |
|
import org.transdroid.daemon.Priority; |
|
import org.transdroid.daemon.Torrent; |
|
import org.transdroid.daemon.TorrentDetails; |
|
import org.transdroid.daemon.TorrentFile; |
|
import org.transdroid.daemon.TorrentStatus; |
|
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.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.SetFilePriorityTask; |
|
import org.transdroid.daemon.task.SetLabelTask; |
|
import org.transdroid.daemon.task.SetTransferRatesTask; |
|
import org.transdroid.daemon.util.HttpHelper; |
|
|
|
import java.io.ByteArrayOutputStream; |
|
import java.io.File; |
|
import java.io.FileInputStream; |
|
import java.io.FileNotFoundException; |
|
import java.io.IOException; |
|
import java.io.UnsupportedEncodingException; |
|
import java.net.MalformedURLException; |
|
import java.net.URI; |
|
import java.net.URLDecoder; |
|
import java.util.ArrayList; |
|
import java.util.Date; |
|
import java.util.HashMap; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Map.Entry; |
|
|
|
/** |
|
* An adapter that allows for easy access to rTorrent torrent data. Communication is handled via the XML-RPC protocol as |
|
* implemented by the aXMLRPC library. |
|
* |
|
* @author erickok |
|
*/ |
|
public class RTorrentAdapter implements IDaemonAdapter { |
|
|
|
private static final String LOG_NAME = "rTorrent daemon"; |
|
|
|
private static final String DEFAULT_RPC_URL = "/RPC2"; |
|
private static final int XMLRPC_MINIMUM_SIZE = 2 * 1024 * 1024; |
|
private static final int XMLRPC_EXTRA_PADDING = 1280; |
|
|
|
private DaemonSettings settings; |
|
private XMLRPCClient rpcclient; |
|
private List<Label> lastKnownLabels = null; |
|
private Integer version = null; |
|
|
|
public RTorrentAdapter(DaemonSettings settings) { |
|
this.settings = settings; |
|
} |
|
|
|
@Override |
|
public DaemonTaskResult executeTask(Log log, DaemonTask task) { |
|
|
|
try { |
|
// Ensure a version number is know to switch to the right methods |
|
if (version == null) { |
|
try { |
|
Object versionObject = makeRtorrentCall(log, "system.client_version", new String[0]); |
|
String[] versionRaw = versionObject.toString().split("\\."); |
|
version = (Integer.parseInt(versionRaw[0]) * 10000) + (Integer.parseInt(versionRaw[1]) * 100) + Integer.parseInt(versionRaw[2]); |
|
} catch (Exception e) { |
|
version = 10000; |
|
} |
|
} |
|
switch (task.getMethod()) { |
|
case Retrieve: |
|
|
|
// @formatter:off |
|
Object result = makeRtorrentCall(log, "d.multicall2", |
|
new String[]{"", "main", |
|
"d.hash=", |
|
"d.name=", |
|
"d.state=", |
|
"d.down.rate=", |
|
"d.up.rate=", |
|
"d.peers_connected=", |
|
"d.peers_not_connected=", |
|
"d.peers_accounted=", |
|
"d.bytes_done=", |
|
"d.up.total=", |
|
"d.size_bytes=", |
|
"d.creation_date=", |
|
"d.left_bytes=", |
|
"d.complete=", |
|
"d.is_active=", |
|
"d.is_hash_checking=", |
|
"d.is_multi_file=", |
|
"d.base_filename=", |
|
"d.message=", |
|
"d.custom=addtime", |
|
"d.custom=seedingtime", |
|
"d.custom1=", |
|
"d.peers_complete=", |
|
"d.peers_accounted=", |
|
"d.is_open="}); |
|
// @formatter:on |
|
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result), |
|
lastKnownLabels); |
|
|
|
case GetTorrentDetails: |
|
|
|
// @formatter:off |
|
Object dresult = makeRtorrentCall(log, "t.multicall", new String[]{ |
|
task.getTargetTorrent().getUniqueID(), |
|
"", |
|
"t.url="}); |
|
// @formatter:on |
|
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, |
|
onTorrentDetailsRetrieved(log, dresult)); |
|
|
|
case GetFileList: |
|
|
|
// @formatter:off |
|
Object fresult = makeRtorrentCall(log, "f.multicall", new String[]{ |
|
task.getTargetTorrent().getUniqueID(), |
|
"", |
|
"f.path=", |
|
"f.size_bytes=", |
|
"f.priority=", |
|
"f.completed_chunks=", |
|
"f.size_chunks=", |
|
"f.priority=", |
|
"f.frozen_path="}); |
|
// @formatter:on |
|
return new GetFileListTaskSuccessResult((GetFileListTask) task, |
|
onTorrentFilesRetrieved(fresult, task.getTargetTorrent())); |
|
|
|
case AddByFile: |
|
|
|
// Request to add a torrent by local .torrent file |
|
File file = new File(URI.create(((AddByFileTask) task).getFile())); |
|
FileInputStream in = new FileInputStream(file); |
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
byte[] buffer = new byte[(int) file.length()]; |
|
int read; |
|
while ((read = in.read(buffer, 0, buffer.length)) > 0) { |
|
baos.write(buffer, 0, read); |
|
} |
|
byte[] bytes = baos.toByteArray(); |
|
int size = Math.max(((int) file.length() * 2) + XMLRPC_EXTRA_PADDING, XMLRPC_MINIMUM_SIZE); |
|
if (version >= 904) { |
|
makeRtorrentCall(log, "network.xmlrpc.size_limit.set", new Object[]{"", size + XMLRPC_EXTRA_PADDING}); |
|
makeRtorrentCall(log, "load.raw_start", new Object[]{"", bytes}); |
|
} else { |
|
makeRtorrentCall(log, "set_xmlrpc_size_limit", new Object[]{size + XMLRPC_EXTRA_PADDING}); |
|
makeRtorrentCall(log, "load_raw_start", new Object[]{bytes}); |
|
} |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case AddByUrl: |
|
|
|
// Request to add a torrent by URL |
|
String url = ((AddByUrlTask) task).getUrl(); |
|
if (version >= 904) { |
|
makeRtorrentCall(log, "load.start", new String[]{"", url}); |
|
} else { |
|
makeRtorrentCall(log, "load_start", new String[]{url}); |
|
} |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case AddByMagnetUrl: |
|
|
|
// Request to add a magnet link by URL |
|
String magnet = ((AddByMagnetUrlTask) task).getUrl(); |
|
if (version >= 904) { |
|
makeRtorrentCall(log, "load.start", new String[]{"", magnet}); |
|
} else { |
|
makeRtorrentCall(log, "load_start", new String[]{magnet}); |
|
} |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Remove: |
|
|
|
// Remove a torrent |
|
RemoveTask removeTask = (RemoveTask) task; |
|
if (removeTask.includingData()) { |
|
makeRtorrentCall(log, "d.custom5.set", |
|
new String[]{task.getTargetTorrent().getUniqueID(), "1"}); |
|
makeRtorrentCall(log, "d.delete_tied", |
|
new String[]{task.getTargetTorrent().getUniqueID()}); |
|
} |
|
makeRtorrentCall(log, "d.erase", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Pause: |
|
|
|
// Pause a torrent |
|
makeRtorrentCall(log, "d.stop", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case PauseAll: |
|
|
|
// Resume all torrents |
|
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.stop="}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Resume: |
|
|
|
// Resume a torrent |
|
makeRtorrentCall(log, "d.start", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case ResumeAll: |
|
|
|
// Resume all torrents |
|
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.start="}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Stop: |
|
|
|
// Stop a torrent |
|
makeRtorrentCall(log, "d.stop", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
makeRtorrentCall(log, "d.close", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case StopAll: |
|
|
|
// Stop all torrents |
|
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.stop=", "d.close="}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Start: |
|
|
|
// Start a torrent |
|
makeRtorrentCall(log, "d.open", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
makeRtorrentCall(log, "d.start", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case StartAll: |
|
|
|
// Start all torrents |
|
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.open=", "d.start="}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case SetFilePriorities: |
|
|
|
// For each of the chosen files belonging to some torrent, set the priority |
|
SetFilePriorityTask prioTask = (SetFilePriorityTask) task; |
|
String newPriority = "" + convertPriority(prioTask.getNewPriority()); |
|
// One at a time; rTorrent doesn't seem to support a multicall on a selective number of files |
|
for (TorrentFile forFile : prioTask.getForFiles()) { |
|
makeRtorrentCall(log, "f.priority.set", |
|
new String[]{task.getTargetTorrent().getUniqueID() + ":f" + forFile.getKey(), |
|
newPriority}); |
|
} |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case SetTransferRates: |
|
|
|
// Request to set the maximum transfer rates |
|
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task; |
|
makeRtorrentCall(log, "throttle.global_down.max_rate.set", new String[]{"", (ratesTask.getDownloadRate() == null ? "0" : |
|
ratesTask.getDownloadRate().toString() + "k")}); |
|
makeRtorrentCall(log, "throttle.global_up.max_rate.set", new String[]{"", |
|
(ratesTask.getUploadRate() == null ? "0" : ratesTask.getUploadRate().toString() + "k")}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case SetLabel: |
|
|
|
SetLabelTask labelTask = (SetLabelTask) task; |
|
makeRtorrentCall(log, "d.custom1.set", |
|
new String[]{task.getTargetTorrent().getUniqueID(), labelTask.getNewLabel()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case ForceRecheck: |
|
|
|
// Force re-check of data of a torrent |
|
makeRtorrentCall(log, "d.check_hash", new String[]{task.getTargetTorrent().getUniqueID()}); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
default: |
|
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported, |
|
task.getMethod() + " is not supported by " + getType())); |
|
} |
|
} catch (DaemonException e) { |
|
return new DaemonTaskFailureResult(task, e); |
|
} catch (FileNotFoundException e) { |
|
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.FileAccessError, e.toString())); |
|
} catch (IOException e) { |
|
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ConnectionError, e.toString())); |
|
} |
|
} |
|
|
|
private Object makeRtorrentCall(Log log, String serverMethod, Object[] arguments) |
|
throws DaemonException, MalformedURLException { |
|
|
|
// Initialise the HTTP client |
|
initialise(); |
|
|
|
StringBuilder paramsBuilder = new StringBuilder(); |
|
for (Object arg : arguments) { |
|
paramsBuilder.append(" ").append(arg.toString()); |
|
} |
|
String params = paramsBuilder.toString(); |
|
String s = params.length() > 100 ? params.substring(0, 100) + "..." : params; |
|
try { |
|
log.d(LOG_NAME, "Calling " + serverMethod + " with params [" + |
|
s + " ]"); |
|
return rpcclient.call(serverMethod, arguments); |
|
} catch (IllegalArgumentException e) { |
|
log.d(LOG_NAME, "Using " + buildWebUIUrl() + ": " + e.toString()); |
|
throw new DaemonException(ExceptionType.ConnectionError, "Error making call to " + serverMethod); |
|
} catch (XMLRPCException e) { |
|
log.d(LOG_NAME, e.toString()); |
|
if (e.getCause() instanceof UnauthorizdException) { |
|
throw new DaemonException(ExceptionType.AuthenticationFailure, e.toString()); |
|
} |
|
if (e.getCause() instanceof DaemonException) { |
|
throw (DaemonException) e.getCause(); |
|
} |
|
throw new DaemonException(ExceptionType.ConnectionError, |
|
"Error making call to " + serverMethod + " with params [" + |
|
s + " ]: " + |
|
e.toString()); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Instantiates a XML-RPC client with proper credentials. |
|
* |
|
* @throws DaemonException On conflicting settings (i.e. user authentication but no password or username provided) |
|
*/ |
|
private synchronized void initialise() throws DaemonException { |
|
if(rpcclient == null) { |
|
int flags = XMLRPCClient.FLAGS_8BYTE_INT; |
|
this.rpcclient = new XMLRPCClient(HttpHelper.createStandardHttpClient(settings, true), |
|
settings.getAddress(), buildWebUIUrl(), flags); |
|
} |
|
} |
|
|
|
/** |
|
* Build the URL of rTorrent's XML-RPC location from the user settings. |
|
* |
|
* @return The URL of the RPC API |
|
*/ |
|
private String buildWebUIUrl() { |
|
String address = settings.getAddress() == null ? "" : settings.getAddress().trim(); |
|
String folder = settings.getFolder() == null ? "" : settings.getFolder().trim(); |
|
return (settings.getSsl() ? "https://" : "http://") + address + ":" + settings.getPort() + |
|
(TextUtils.isEmpty(folder) ? DEFAULT_RPC_URL : folder); |
|
} |
|
|
|
private List<Torrent> onTorrentsRetrieved(Object response) throws DaemonException { |
|
|
|
if (!(response instanceof Object[])) { |
|
|
|
throw new DaemonException(ExceptionType.ParsingFailed, |
|
"Response on retrieveing torrents did not return a list of objects"); |
|
|
|
} else { |
|
|
|
// Parse torrent list from response |
|
// Formatted as Object[][], see http://libtorrent.rakshasa.no/wiki/RTorrentCommands#Download |
|
List<Torrent> torrents = new ArrayList<>(); |
|
Map<String, Integer> labels = new HashMap<>(); |
|
Object[] responseList = (Object[]) response; |
|
for (int i = 0; i < responseList.length; i++) { |
|
|
|
Object[] info = (Object[]) responseList[i]; |
|
String error = (String) info[18]; |
|
error = error.equals("") ? null : error; |
|
|
|
// Determine the time added |
|
Date added; |
|
Long addtime = null; |
|
try { |
|
addtime = Long.valueOf(((String) info[19]).trim()); |
|
} catch (NumberFormatException e) { |
|
// Not a number (timestamp); ignore and fall back to using creationtime |
|
} |
|
if (addtime != null) |
|
// Successfully received the addtime from rTorrent (which is a String like '1337089336\n') |
|
{ |
|
added = new Date(addtime * 1000L); |
|
} else { |
|
// rTorrent didn't have the addtime (missing plugin?): base it on creationtime instead |
|
if (info[11] instanceof Long) { |
|
added = new Date((Long) info[11] * 1000L); |
|
} else { |
|
added = new Date((Integer) info[11] * 1000L); |
|
} |
|
} |
|
|
|
// Determine the seeding time |
|
Date finished = null; |
|
Long seedingtime = null; |
|
try { |
|
seedingtime = Long.valueOf(((String) info[20]).trim()); |
|
} catch (NumberFormatException e) { |
|
// Not a number (timestamp); ignore and fall back to using creationtime |
|
} |
|
if (seedingtime != null) |
|
// Successfully received the seedingtime from rTorrent (which is a String like '1337089336\n') |
|
{ |
|
finished = new Date(seedingtime * 1000L); |
|
} |
|
|
|
// Determine the label |
|
String label = null; |
|
try { |
|
label = URLDecoder.decode((String) info[21], "UTF-8"); |
|
if (labels.containsKey(label)) { |
|
labels.put(label, labels.get(label) + 1); |
|
} else { |
|
labels.put(label, 0); |
|
} |
|
} catch (UnsupportedEncodingException e) { |
|
// Can't decode label name; ignore it |
|
} |
|
|
|
String baseFilename = info[17] + "/"; |
|
|
|
if (info[3] instanceof Long) { |
|
|
|
// rTorrent uses the i8 dialect which returns 64-bit integers |
|
long rateDownload = (Long) info[3]; |
|
|
|
// @formatter:off |
|
torrents.add(new Torrent( |
|
i, |
|
(String) info[0], // hash |
|
(String) info[1], // name |
|
convertTorrentStatus((Long) info[2], (Long) info[24], (Long) info[13], (Long) info[14], (Long) info[15]), // status |
|
(((Long) info[16]) == 1) ? baseFilename : "", // multi file? base_filename else "" |
|
((Long) info[3]).intValue(), // rateDownload |
|
((Long) info[4]).intValue(), // rateUpload |
|
((Long) info[22]).intValue(), // seedersConnected |
|
((Long) info[5]).intValue() + ((Long) info[6]).intValue(), // seedersKnown |
|
((Long) info[23]).intValue(), // leechersConnected |
|
((Long) info[5]).intValue() + ((Long) info[6]).intValue(), // leechersKnown |
|
(rateDownload > 0 ? (int) (((Long) info[12]) / rateDownload) : -1), // eta (bytes left / rate download, if rate > 0) |
|
(Long) info[8], // downloadedEver |
|
(Long) info[9], // uploadedEver |
|
(Long) info[10], // totalSize |
|
((Long) info[8]).floatValue() / ((Long) info[10]).floatValue(), // partDone |
|
0f, // TODO: Add availability data |
|
label, |
|
added, |
|
finished, |
|
error, |
|
settings.getType())); |
|
// @formatter:on |
|
|
|
} else { |
|
|
|
// rTorrent uses the default dialect with 32-bit integers |
|
int rateDownload = (Integer) info[3]; |
|
|
|
// @formatter:off |
|
torrents.add(new Torrent( |
|
i, |
|
(String) info[0], // hash |
|
(String) info[1], // name |
|
convertTorrentStatus(((Integer) info[2]).longValue(), ((Integer) info[24]).longValue(), ((Integer) info[13]).longValue(), ((Integer) info[14]).longValue(), ((Integer) info[15]).longValue()), // status |
|
(((Integer) info[16]) == 1) ? baseFilename : "", // multi file? base_filename else "" |
|
rateDownload, // rateDownload |
|
(Integer) info[4], // rateUpload |
|
(Integer) info[22], // seedersConnected |
|
(Integer) info[5] + (Integer) info[6], // seedersKnown |
|
(Integer) info[23], // leechersConnected |
|
(Integer) info[5] + (Integer) info[6], // leechersKnown |
|
(rateDownload > 0 ? (Integer) info[12] / rateDownload : -1), // eta (bytes left / rate download, if rate > 0) |
|
(Integer) info[8], // downloadedEver |
|
(Integer) info[9], // uploadedEver |
|
(Integer) info[10], // totalSize |
|
((Integer) info[8]).floatValue() / ((Integer) info[10]).floatValue(), // partDone |
|
0f, // TODO: Add availability data |
|
label, |
|
added, |
|
finished, |
|
error, |
|
settings.getType())); |
|
// @formatter:on |
|
|
|
} |
|
} |
|
lastKnownLabels = new ArrayList<>(); |
|
for (Entry<String, Integer> pair : labels.entrySet()) { |
|
if (pair.getKey() != null) { |
|
lastKnownLabels.add(new Label(pair.getKey(), pair.getValue())); |
|
} |
|
} |
|
return torrents; |
|
|
|
} |
|
|
|
} |
|
|
|
private List<TorrentFile> onTorrentFilesRetrieved(Object response, Torrent torrent) throws DaemonException { |
|
|
|
if (!(response instanceof Object[])) { |
|
|
|
throw new DaemonException(ExceptionType.ParsingFailed, |
|
"Response on retrieveing torrent files did not return a list of objects"); |
|
|
|
} else { |
|
|
|
// Parse torrent files from response |
|
// Formatted as Object[][], see http://libtorrent.rakshasa.no/wiki/RTorrentCommands#Download |
|
List<TorrentFile> files = new ArrayList<>(); |
|
Object[] responseList = (Object[]) response; |
|
for (int i = 0; i < responseList.length; i++) { |
|
|
|
Object[] info = (Object[]) responseList[i]; |
|
if (info[1] instanceof Long) { |
|
|
|
// rTorrent uses the i8 dialect which returns 64-bit integers |
|
Long size = (Long) info[1]; |
|
Long chunksDone = (Long) info[3]; |
|
Long chunksTotal = (Long) info[4]; |
|
Long priority = (Long) info[5]; |
|
|
|
// @formatter:off |
|
files.add(new TorrentFile( |
|
"" + i, |
|
(String) info[0], // name |
|
torrent.getLocationDir() + info[0], // torrent locationDir + file name |
|
(String) info[6], // fullPath |
|
size, // size |
|
(long) (size * ((float) chunksDone / (float) chunksTotal)), // done |
|
convertRtorrentPriority(priority.intValue()))); // priority |
|
// @formatter:on |
|
|
|
} else { |
|
|
|
// rTorrent uses the default dialect with 32-bit integers |
|
Integer size = (Integer) info[1]; |
|
Integer chunksDone = (Integer) info[3]; |
|
Integer chunksTotal = (Integer) info[4]; |
|
Integer priority = (Integer) info[5]; |
|
|
|
// @formatter:off |
|
files.add(new TorrentFile( |
|
"" + i, |
|
(String) info[0], // name |
|
torrent.getLocationDir() + "/" + info[0], // torrent locationDir + file name |
|
(String) info[0], // fullPath |
|
size, // size |
|
(int) (size * ((float) chunksDone / (float) chunksTotal)), // done |
|
convertRtorrentPriority(priority))); // priority |
|
// @formatter:on |
|
|
|
} |
|
} |
|
return files; |
|
|
|
} |
|
|
|
} |
|
|
|
private Priority convertRtorrentPriority(int code) { |
|
// Note that Rtorrent has no low priority value |
|
switch (code) { |
|
case 0: |
|
return Priority.Off; |
|
case 2: |
|
return Priority.High; |
|
default: |
|
return Priority.Normal; |
|
} |
|
} |
|
|
|
private int convertPriority(Priority priority) { |
|
// Note that Rtorrent has no low priority value |
|
switch (priority) { |
|
case Off: |
|
return 0; |
|
case High: |
|
return 2; |
|
default: |
|
return 1; |
|
} |
|
} |
|
|
|
private TorrentStatus convertTorrentStatus(Long state, Long open, Long complete, Long active, Long checking) { |
|
if (checking == 1) { |
|
return TorrentStatus.Checking; |
|
} |
|
|
|
if (open == 1) { |
|
// links with tracker/peers are open: not stopped |
|
if (state == 1 && active == 1) { |
|
if (complete == 1) { |
|
return TorrentStatus.Seeding; |
|
} else { |
|
return TorrentStatus.Downloading; |
|
} |
|
} |
|
return TorrentStatus.Paused; |
|
} else { |
|
// maybe could be Stopped or Waiting |
|
return TorrentStatus.Queued; |
|
} |
|
} |
|
|
|
private TorrentDetails onTorrentDetailsRetrieved(Log log, Object response) throws DaemonException { |
|
|
|
if (!(response instanceof Object[])) { |
|
|
|
throw new DaemonException(ExceptionType.ParsingFailed, |
|
"Response on retrieveing trackers did not return a list of objects"); |
|
|
|
} else { |
|
|
|
// Parse a torrent's trackers from response |
|
// Formatted as Object[][], see http://libtorrent.rakshasa.no/wiki/RTorrentCommands#Download |
|
List<String> trackers = new ArrayList<>(); |
|
Object[] responseList = (Object[]) response; |
|
try { |
|
for (Object aResponseList : responseList) { |
|
Object[] info = (Object[]) aResponseList; |
|
trackers.add((String) info[0]); |
|
} |
|
} catch (Exception e) { |
|
log.e(LOG_NAME, e.toString()); |
|
} |
|
return new TorrentDetails(trackers, null); |
|
|
|
} |
|
|
|
} |
|
|
|
@Override |
|
public Daemon getType() { |
|
return settings.getType(); |
|
} |
|
|
|
@Override |
|
public DaemonSettings getSettings() { |
|
return this.settings; |
|
} |
|
|
|
}
|
|
|