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.
805 lines
34 KiB
805 lines
34 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.bitComet; |
|
|
|
import com.android.internal.http.multipart.MultipartEntity; |
|
import com.android.internal.http.multipart.Part; |
|
import org.apache.http.HttpEntity; |
|
import org.apache.http.HttpResponse; |
|
import org.apache.http.HttpStatus; |
|
import org.apache.http.NameValuePair; |
|
import org.apache.http.client.entity.UrlEncodedFormEntity; |
|
import org.apache.http.client.methods.HttpGet; |
|
import org.apache.http.client.methods.HttpPost; |
|
import org.apache.http.impl.client.DefaultHttpClient; |
|
import org.apache.http.message.BasicNameValuePair; |
|
import org.apache.http.protocol.HTTP; |
|
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; |
|
import org.transdroid.daemon.Torrent; |
|
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.RemoveTask; |
|
import org.transdroid.daemon.task.RetrieveTask; |
|
import org.transdroid.daemon.task.RetrieveTaskSuccessResult; |
|
import org.transdroid.daemon.task.SetTransferRatesTask; |
|
import org.transdroid.daemon.util.HttpHelper; |
|
import org.transdroid.multipart.BitCometFilePart; |
|
import org.transdroid.multipart.Utf8StringPart; |
|
import org.xmlpull.v1.XmlPullParser; |
|
import org.xmlpull.v1.XmlPullParserException; |
|
import org.xmlpull.v1.XmlPullParserFactory; |
|
|
|
import java.io.File; |
|
import java.io.FileNotFoundException; |
|
import java.io.StringReader; |
|
import java.io.UnsupportedEncodingException; |
|
import java.net.URI; |
|
import java.text.DateFormat; |
|
import java.text.SimpleDateFormat; |
|
import java.util.ArrayList; |
|
import java.util.Date; |
|
import java.util.List; |
|
|
|
|
|
/** |
|
* The daemon adapter for the BitComet torrent client. |
|
* |
|
* @author SeNS (sensboston) |
|
* <p/> |
|
* 09/26/2012: added AJAX support for BitComet v.1.34 and up : added additional tasks support |
|
*/ |
|
public class BitCometAdapter implements IDaemonAdapter { |
|
|
|
private static final String LOG_NAME = "BitComet daemon"; |
|
|
|
private DaemonSettings settings; |
|
private DefaultHttpClient httpclient; |
|
|
|
public BitCometAdapter(DaemonSettings settings) { |
|
this.settings = settings; |
|
} |
|
|
|
/** |
|
* Returns the size of the torrent, as parsed form some string |
|
* |
|
* @param size The size in a string format, i.e. '691 MB' |
|
* @return The size in bytes |
|
*/ |
|
private static long convertSize(String size) { |
|
try { |
|
if (size.endsWith("GB")) { |
|
return (long) (Float.parseFloat(size.substring(0, size.indexOf("GB"))) * 1024 * 1024 * 1024); |
|
} else if (size.endsWith("MB")) { |
|
return (long) (Float.parseFloat(size.substring(0, size.indexOf("MB"))) * 1024 * 1024); |
|
} else if (size.endsWith("kB")) { |
|
return (long) (Float.parseFloat(size.substring(0, size.indexOf("kB"))) * 1024); |
|
} else if (size.endsWith("B")) { |
|
return (long) (Float.parseFloat(size.substring(0, size.indexOf("B")))); |
|
} |
|
} catch (Exception ignored) { |
|
} |
|
return 0; |
|
} |
|
|
|
/** |
|
* Returns the part done (or progress) of a torrent, as parsed from some string |
|
* |
|
* @param progress The part done in a string format, i.e. '15.96' |
|
* @return The part done as [0..1] fraction, i.e. 0.1596 |
|
*/ |
|
public static float convertProgress(String progress) { |
|
return Float.parseFloat(progress) / 1000.0f; |
|
} |
|
|
|
@Override |
|
public DaemonTaskResult executeTask(Log log, DaemonTask task) { |
|
|
|
try { |
|
switch (task.getMethod()) { |
|
case Retrieve: |
|
|
|
// Request all torrents from server |
|
// first, check client for the new AJAX interface (BitComet v.1.34 and up) |
|
try { |
|
String xmlResult = makeRequest(log, "/panel/task_list_xml"); |
|
if (xmlResult.startsWith("<?xml")) { |
|
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseXmlTorrents(xmlResult), |
|
null); |
|
} |
|
} catch (Exception e) { |
|
// it's probably an old client, parse HTML instead |
|
String htmlResult = makeRequest(log, "/panel/task_list"); |
|
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseHttpTorrents(log, htmlResult), |
|
null); |
|
} |
|
|
|
case GetFileList: |
|
|
|
// Request files listing for a specific torrent |
|
String fhash = task.getTargetTorrent().getUniqueID(); |
|
String fileListResult = makeRequest(log, "/panel/task_detail", new BasicNameValuePair("id", fhash), |
|
new BasicNameValuePair("show", "files")); |
|
return new GetFileListTaskSuccessResult((GetFileListTask) task, |
|
parseHttpTorrentFiles(fileListResult, fhash)); |
|
|
|
case AddByFile: |
|
|
|
// Upload a local .torrent file |
|
String ufile = ((AddByFileTask) task).getFile(); |
|
makeFileUploadRequest(log, "/panel/task_add_bt_result", ufile); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case AddByUrl: |
|
|
|
// Request to add a torrent by URL |
|
String url = ((AddByUrlTask) task).getUrl(); |
|
makeUploadUrlRequest(log, "/panel/task_add_magnet_result", url); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case AddByMagnetUrl: |
|
|
|
// Request to add a torrent by URL |
|
String magnetUrl = ((AddByMagnetUrlTask) task).getUrl(); |
|
makeUploadUrlRequest(log, "/panel/task_add_magnet_result", magnetUrl); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Remove: |
|
|
|
// Remove a torrent |
|
RemoveTask removeTask = (RemoveTask) task; |
|
makeRequest(log, "/panel/task_delete", |
|
new BasicNameValuePair("id", removeTask.getTargetTorrent().getUniqueID()), |
|
new BasicNameValuePair("action", |
|
(removeTask.includingData() ? "delete_all" : "delete_task"))); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Pause: |
|
|
|
// Pause a torrent |
|
makeRequest(log, "/panel/task_action", |
|
new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()), |
|
new BasicNameValuePair("action", "stop")); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case Resume: |
|
|
|
// Resume a torrent |
|
makeRequest(log, "/panel/task_action", |
|
new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()), |
|
new BasicNameValuePair("action", "start")); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case PauseAll: |
|
|
|
// Suspend (pause) all active torrents |
|
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "suspend_all")); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case ResumeAll: |
|
|
|
// Resume suspended torrents |
|
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "resume_all")); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case StopAll: |
|
|
|
// Stop all torrents |
|
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "stop_all")); |
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case StartAll: |
|
|
|
// Start all torrents for download and seeding |
|
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "start_all_download")); |
|
makeRequest(log, "/panel/tasklist_action", new BasicNameValuePair("id", "start_all_seeding")); |
|
|
|
return new DaemonTaskSuccessResult(task); |
|
|
|
case SetTransferRates: |
|
|
|
// Request to set the maximum transfer rates |
|
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task; |
|
String dl = |
|
Integer.toString((ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate())); |
|
String ul = Integer.toString((ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate())); |
|
makeRequest(log, "/panel/option_set", new BasicNameValuePair("key", "down_rate_max"), |
|
new BasicNameValuePair("value", dl)); |
|
makeRequest(log, "/panel/option_set", new BasicNameValuePair("key", "up_rate_max"), |
|
new BasicNameValuePair("value", ul)); |
|
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, new DaemonException(ExceptionType.ParsingFailed, e.toString())); |
|
} |
|
} |
|
|
|
/** |
|
* Instantiates an HTTP client with proper credentials that can be used for all Buffalo NAS requests. |
|
* |
|
* @throws DaemonException On conflicting or missing settings |
|
*/ |
|
private synchronized void initialise() throws DaemonException { |
|
if(httpclient == null) { |
|
httpclient = HttpHelper.createStandardHttpClient(settings, true); |
|
} |
|
} |
|
|
|
/** |
|
* Build the URL of the HTTP request from the user settings |
|
* |
|
* @return The URL to request |
|
*/ |
|
private String buildWebUIUrl(String path) { |
|
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + path; |
|
} |
|
|
|
private String makeRequest(Log log, String url, NameValuePair... params) throws DaemonException { |
|
|
|
try { |
|
|
|
// Initialize the HTTP client |
|
initialise(); |
|
|
|
// Add the parameters to the query string |
|
boolean first = true; |
|
StringBuilder urlBuilder = new StringBuilder(url); |
|
for (NameValuePair param : params) { |
|
if (first) { |
|
urlBuilder.append("?"); |
|
first = false; |
|
} else { |
|
urlBuilder.append("&"); |
|
} |
|
urlBuilder.append(param.getName()).append("=").append(param.getValue()); |
|
} |
|
url = urlBuilder.toString(); |
|
|
|
// Make the request |
|
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl(url))); |
|
HttpEntity entity = response.getEntity(); |
|
if (entity != null) { |
|
|
|
// Read HTTP response |
|
java.io.InputStream instream = entity.getContent(); |
|
String result = HttpHelper.convertStreamToString(instream); |
|
instream.close(); |
|
|
|
// Return raw result |
|
return result; |
|
} |
|
|
|
log.d(LOG_NAME, "Error: No entity in HTTP response"); |
|
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response."); |
|
|
|
} catch (UnsupportedEncodingException e) { |
|
throw new DaemonException(ExceptionType.ConnectionError, e.toString()); |
|
} catch (Exception e) { |
|
log.d(LOG_NAME, "Error: " + e.toString()); |
|
throw new DaemonException(ExceptionType.ConnectionError, e.toString()); |
|
} |
|
|
|
} |
|
|
|
private boolean makeFileUploadRequest(Log log, String path, String file) throws DaemonException { |
|
|
|
try { |
|
|
|
// Initialize the HTTP client |
|
if (httpclient == null) { |
|
initialise(); |
|
} |
|
|
|
// Get default download file location first |
|
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl("/panel/task_add_bt"))); |
|
HttpEntity entity = response.getEntity(); |
|
if (entity != null) { |
|
|
|
// Read BitComet response |
|
java.io.InputStream instream = entity.getContent(); |
|
String result = HttpHelper.convertStreamToString(instream); |
|
instream.close(); |
|
|
|
int idx = result.indexOf("save_path' value='") + 18; |
|
String defaultPath = result.substring(idx, result.indexOf("'>", idx)); |
|
|
|
// Setup request using POST |
|
HttpPost httppost = new HttpPost(buildWebUIUrl(path)); |
|
File upload = new File(URI.create(file)); |
|
Part[] parts = |
|
{new BitCometFilePart("torrent_file", upload), new Utf8StringPart("save_path", defaultPath)}; |
|
httppost.setEntity(new MultipartEntity(parts, httppost.getParams())); |
|
|
|
// Make the request |
|
response = httpclient.execute(httppost); |
|
|
|
entity = response.getEntity(); |
|
if (entity != null) { |
|
// Check BitComet response |
|
instream = entity.getContent(); |
|
result = HttpHelper.convertStreamToString(instream); |
|
instream.close(); |
|
if (result.indexOf("failed!") > 0) { |
|
throw new Exception("Adding torrent file failed"); |
|
} |
|
} |
|
|
|
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK; |
|
} |
|
return false; |
|
|
|
} catch (FileNotFoundException e) { |
|
throw new DaemonException(ExceptionType.FileAccessError, e.toString()); |
|
} catch (Exception e) { |
|
log.d(LOG_NAME, "Error: " + e.toString()); |
|
throw new DaemonException(ExceptionType.ConnectionError, e.toString()); |
|
} |
|
} |
|
|
|
private boolean makeUploadUrlRequest(Log log, String path, String url) throws DaemonException { |
|
|
|
try { |
|
|
|
// Initialize the HTTP client |
|
if (httpclient == null) { |
|
initialise(); |
|
} |
|
|
|
// Get default download file location first |
|
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl("/panel/task_add_httpftp"))); |
|
HttpEntity entity = response.getEntity(); |
|
if (entity != null) { |
|
|
|
// Read BitComet response |
|
java.io.InputStream instream = entity.getContent(); |
|
String result = HttpHelper.convertStreamToString(instream); |
|
instream.close(); |
|
|
|
int idx = result.indexOf("save_path' value='") + 18; |
|
String defaultPath = result.substring(idx, result.indexOf("'>", idx)); |
|
|
|
// Setup form fields and post request |
|
HttpPost httppost = new HttpPost(buildWebUIUrl(path)); |
|
|
|
List<NameValuePair> params = new ArrayList<>(); |
|
params.add(new BasicNameValuePair("url", url)); |
|
params.add(new BasicNameValuePair("save_path", defaultPath)); |
|
params.add(new BasicNameValuePair("connection", "5")); |
|
params.add(new BasicNameValuePair("ReferPage", "")); |
|
params.add(new BasicNameValuePair("textSpeedLimit", "0")); |
|
httppost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); |
|
|
|
// Make the request |
|
response = httpclient.execute(httppost); |
|
|
|
entity = response.getEntity(); |
|
if (entity != null) { |
|
// Check BitComet response |
|
instream = entity.getContent(); |
|
result = HttpHelper.convertStreamToString(instream); |
|
instream.close(); |
|
if (result.indexOf("failed!") > 0) { |
|
throw new Exception("Adding URL failed"); |
|
} |
|
} |
|
|
|
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK; |
|
} |
|
return false; |
|
} catch (Exception e) { |
|
log.d(LOG_NAME, "Error: " + e.toString()); |
|
throw new DaemonException(ExceptionType.ConnectionError, e.toString()); |
|
} |
|
} |
|
|
|
/** |
|
* Parse BitComet HTML page (http response) |
|
* |
|
* @param response The raw HTML response from the server |
|
* @return The parsed list of torrents from the raw HTML content |
|
* @throws DaemonException |
|
*/ |
|
private ArrayList<Torrent> parseHttpTorrents(Log log, String response) throws DaemonException { |
|
|
|
ArrayList<Torrent> torrents = new ArrayList<>(); |
|
|
|
try { |
|
|
|
// Find, prepare and split substring with HTML tag TABLE |
|
String[] parts = |
|
response.substring(response.indexOf("<TABLE"), response.indexOf("</TABLE>")).replaceAll("</td>", "") |
|
.replaceAll("</tr>", "").replaceAll("\n", "").split("<tr>"); |
|
|
|
for (int i = 2; i < parts.length; i++) { |
|
|
|
String[] subParts = parts[i].replaceAll("<td>", "<td").split("<td"); |
|
|
|
if (subParts.length == 10 && subParts[1].contains("BT")) { |
|
|
|
String name = subParts[2].substring(subParts[2].indexOf("/panel/task_detail")); |
|
name = name.substring(name.indexOf(">") + 1, name.indexOf("<")); |
|
|
|
TorrentStatus status = convertStatus(subParts[3]); |
|
String percenDoneStr = subParts[6]; |
|
String downloadRateStr = subParts[7]; |
|
String uploadRateStr = subParts[8]; |
|
|
|
long size = convertSize(subParts[5]); |
|
float percentDone = Float.parseFloat(percenDoneStr.substring(0, percenDoneStr.indexOf("%"))); |
|
long sizeDone = (long) (size * percentDone / 100); |
|
|
|
int rateUp = 1000 * Integer.parseInt(uploadRateStr.substring(0, uploadRateStr.indexOf("kB/s"))); |
|
int rateDown = |
|
1000 * Integer.parseInt(downloadRateStr.substring(0, downloadRateStr.indexOf("kB/s"))); |
|
|
|
// Unfortunately, there is no info for above values providing by BitComet now, |
|
// so we may only send additional request for that |
|
int leechers = 0; |
|
int seeders = 0; |
|
int knownLeechers = 0; |
|
int knownSeeders = 0; |
|
int distributed_copies = 0; |
|
long sizeUp; |
|
String comment; |
|
Date dateAdded; |
|
|
|
// Comment code below to speedup torrent listing |
|
// P.S. feature request to extend torrents info is already sent to the BitComet developers |
|
//* |
|
// Lets make summary request and parse details |
|
String summary = makeRequest(log, "/panel/task_detail", new BasicNameValuePair("id", "" + (i - 2)), |
|
new BasicNameValuePair("show", "summary")); |
|
|
|
String[] sumParts = summary.substring(summary.indexOf("<div align=\"left\">Value</div></th>")) |
|
.split("<tr><td>"); |
|
comment = sumParts[7].substring(sumParts[7].indexOf("<td>") + 4, sumParts[7].indexOf("</td></tr>")); |
|
|
|
// Indexes for date and uploaded size |
|
int idx = 9; |
|
int sizeIdx = 12; |
|
|
|
if (status == TorrentStatus.Downloading) { |
|
seeders = Integer.parseInt(sumParts[9] |
|
.substring(sumParts[9].indexOf("Seeds:") + 6, sumParts[9].indexOf("(Max possible"))); |
|
leechers = Integer.parseInt(sumParts[9].substring(sumParts[9].indexOf("Peers:") + 6, |
|
sumParts[9].lastIndexOf("(Max possible"))); |
|
knownSeeders = Integer.parseInt(sumParts[9] |
|
.substring(sumParts[9].indexOf("(Max possible:") + 14, sumParts[9].indexOf(")"))); |
|
knownLeechers = Integer.parseInt(sumParts[9] |
|
.substring(sumParts[9].lastIndexOf("(Max possible:") + 14, |
|
sumParts[9].lastIndexOf(")"))); |
|
idx = 13; |
|
sizeIdx = 16; |
|
} |
|
|
|
DateFormat df = new SimpleDateFormat("yyyy-mm-dd kk:mm:ss"); |
|
dateAdded = df.parse(sumParts[idx] |
|
.substring(sumParts[idx].indexOf("<td>") + 4, sumParts[idx].indexOf("</td></tr>"))); |
|
//sizeDone = convertSize(sumParts[sizeIdx].substring(sumParts[sizeIdx].indexOf("<td>")+4, sumParts[sizeIdx].indexOf(" ("))); |
|
sizeUp = convertSize(sumParts[sizeIdx + 1] |
|
.substring(sumParts[sizeIdx + 1].indexOf("<td>") + 4, sumParts[sizeIdx + 1].indexOf(" ("))); |
|
//* |
|
|
|
// Add the parsed torrent to the list |
|
// @formatter:off |
|
torrents.add(new Torrent( |
|
(long) i - 2, |
|
null, |
|
name, |
|
status, |
|
null, |
|
rateDown, |
|
rateUp, |
|
seeders, |
|
knownSeeders, |
|
leechers, |
|
knownLeechers, |
|
(rateDown == 0 ? -1 : (int) ((size - sizeDone) / rateDown)), |
|
sizeDone, |
|
sizeUp, |
|
size, |
|
percentDone / 100, |
|
distributed_copies, |
|
comment, |
|
dateAdded, |
|
null, |
|
null, |
|
settings.getType())); |
|
// @formatter:on |
|
} |
|
} |
|
} catch (Exception e) { |
|
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response."); |
|
} |
|
|
|
return torrents; |
|
} |
|
|
|
/** |
|
* Parse BitComet AJAX response that code was copy-pasted and slightly modified from \Ktorrent\StatsParser.java |
|
* |
|
* @param response The raw XML data as string that was returned by the server |
|
* @return The parsed list of torrents from the XML |
|
* @throws DaemonException |
|
*/ |
|
private ArrayList<Torrent> parseXmlTorrents(String response) throws DaemonException { |
|
|
|
ArrayList<Torrent> torrents = new ArrayList<>(); |
|
|
|
try { |
|
// Use a PullParser to handle XML tags one by one |
|
XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); |
|
xpp.setInput(new StringReader(response)); |
|
|
|
// Temp variables to load into torrent objects |
|
int id = 0; |
|
String name = ""; |
|
@SuppressWarnings("unused") String hash = ""; |
|
TorrentStatus status = TorrentStatus.Unknown; |
|
long sizeDone = 0; |
|
long sizeUp = 0; |
|
long totalSize = 0; |
|
int rateDown = 0; |
|
int rateUp = 0; |
|
int seeders = 0; |
|
int seedersTotal = 0; |
|
int leechers = 0; |
|
int leechersTotal = 0; |
|
float progress = 0; |
|
String label = ""; |
|
Date dateAdded = new Date(); |
|
|
|
// Start pulling |
|
int next = xpp.nextTag(); |
|
String tagName = xpp.getName(); |
|
|
|
while (next != XmlPullParser.END_DOCUMENT) { |
|
|
|
if (next == XmlPullParser.END_TAG && tagName.equals("task")) { |
|
|
|
// End of a 'transfer' item, add gathered torrent data |
|
sizeDone = (long) (totalSize * progress); |
|
|
|
// Fix seeding status for completed torrents |
|
if (status == TorrentStatus.Downloading && progress >= 1) { |
|
status = TorrentStatus.Seeding; |
|
} |
|
|
|
// @formatter:off |
|
torrents.add(new Torrent( |
|
id, |
|
null, // hash, // we suppose to use simple integer IDs |
|
name, |
|
status, |
|
null, |
|
rateDown, |
|
rateUp, |
|
seeders, |
|
seedersTotal, |
|
leechers, |
|
leechersTotal, |
|
(int) ((status == TorrentStatus.Downloading && rateDown != 0) ? (totalSize - sizeDone) / rateDown : -1), // eta (in seconds) = (total_size_in_btes - bytes_already_downloaded) / bytes_per_second |
|
sizeDone, |
|
sizeUp, |
|
totalSize, |
|
progress, |
|
0f, |
|
label, |
|
dateAdded, |
|
null, |
|
null, // Not supported in the web interface |
|
settings.getType())); |
|
// @formatter:on |
|
|
|
} else if (next == XmlPullParser.START_TAG && tagName.equals("task")) { |
|
|
|
// Start of a new 'transfer' item; reset gathered torrent data |
|
name = ""; |
|
//hash = ""; |
|
status = TorrentStatus.Unknown; |
|
sizeDone = 0; |
|
sizeUp = 0; |
|
totalSize = 0; |
|
rateDown = 0; |
|
rateUp = 0; |
|
seeders = 0; |
|
seedersTotal = 0; |
|
leechers = 0; |
|
leechersTotal = 0; |
|
progress = 0; |
|
label = ""; |
|
dateAdded = new Date(); |
|
|
|
} else if (next == XmlPullParser.START_TAG) { |
|
|
|
// Probably encountered a torrent property, i.e. '<type>BT</type>' |
|
next = xpp.next(); |
|
if (next == XmlPullParser.TEXT) { |
|
switch (tagName) { |
|
case "name": |
|
name = xpp.getText().trim(); |
|
break; |
|
case "id": |
|
id = Integer.parseInt(xpp.getText().trim()); |
|
break; |
|
case "infohash": |
|
hash = xpp.getText().trim(); |
|
break; |
|
case "state": |
|
status = convertStatus(xpp.getText()); |
|
break; |
|
case "bytes_downloaded": |
|
sizeDone = Long.parseLong(xpp.getText()); |
|
break; |
|
case "bytes_uploaded": |
|
sizeUp = Long.parseLong(xpp.getText()); |
|
break; |
|
case "size": |
|
totalSize = Long.parseLong(xpp.getText()); |
|
break; |
|
case "down_speed": |
|
rateDown = Integer.parseInt(xpp.getText()); |
|
break; |
|
case "up_speed": |
|
rateUp = Integer.parseInt(xpp.getText()); |
|
break; |
|
case "seeders": |
|
seeders = Integer.parseInt(xpp.getText()); |
|
break; |
|
case "total_seeders": |
|
seedersTotal = Integer.parseInt(xpp.getText()); |
|
break; |
|
case "peers": |
|
leechers = Integer.parseInt(xpp.getText()); |
|
break; |
|
case "total_peers": |
|
leechersTotal = Integer.parseInt(xpp.getText()); |
|
break; |
|
case "progress_permillage": |
|
progress = convertProgress(xpp.getText()); |
|
break; |
|
case "created_time": |
|
dateAdded = new Date(Long.parseLong(xpp.getText())); |
|
break; |
|
case "comment": |
|
label = xpp.getText().trim(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
next = xpp.next(); |
|
if (next == XmlPullParser.START_TAG || next == XmlPullParser.END_TAG) { |
|
tagName = xpp.getName(); |
|
} |
|
} |
|
|
|
} catch (XmlPullParserException e) { |
|
throw new DaemonException(ExceptionType.ParsingFailed, e.toString()); |
|
} catch (Exception e) { |
|
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response."); |
|
} |
|
|
|
return torrents; |
|
} |
|
|
|
/** |
|
* Parse BitComet HTML page (HTTP response) |
|
* |
|
* @param response The raw HTML response from the server |
|
* @return The parsed list of files in the torrent from the raw HTML |
|
* @throws DaemonException |
|
*/ |
|
private ArrayList<TorrentFile> parseHttpTorrentFiles(String response, String hash) throws DaemonException { |
|
|
|
// Parse response |
|
ArrayList<TorrentFile> torrentfiles = new ArrayList<>(); |
|
|
|
try { |
|
|
|
String[] files = response.substring(response.indexOf("Operation Method</div></th>") + 27, |
|
response.lastIndexOf("</TABLE>")).replaceAll("</td>", "").replaceAll("</tr>", "").split("<tr>"); |
|
|
|
for (int i = 1; i < files.length; i++) { |
|
|
|
String[] fileDetails = files[i].replace(">", "").split("<td"); |
|
|
|
long size = convertSize(fileDetails[4].substring(fileDetails[4].indexOf("   ") + 11)); |
|
long sizeDone = 0; |
|
if (!fileDetails[2].contains("--")) { |
|
double percentDone = Double.parseDouble(fileDetails[2].substring(0, fileDetails[2].indexOf("%"))); |
|
sizeDone = (long) (size / 100.0 * percentDone); |
|
} |
|
|
|
// @formatter:off |
|
torrentfiles.add(new TorrentFile( |
|
hash, |
|
fileDetails[3], |
|
fileDetails[3], |
|
settings.getDownloadDir() + fileDetails[3], |
|
size, |
|
sizeDone, |
|
convertPriority(fileDetails[1]))); |
|
// @formatter:on |
|
} |
|
} catch (Exception e) { |
|
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response."); |
|
} |
|
|
|
// Return the list |
|
return torrentfiles; |
|
} |
|
|
|
/** |
|
* Parse BitComet torrent files priority |
|
*/ |
|
private Priority convertPriority(String priority) { |
|
if (priority.equals("Very High") || priority.equals("High")) { |
|
return Priority.High; |
|
} else if (priority.equals("Normal")) { |
|
return Priority.Normal; |
|
} |
|
return Priority.Off; |
|
} |
|
|
|
/** |
|
* Parse BitComet torrent status |
|
*/ |
|
private TorrentStatus convertStatus(String state) { |
|
// Status is given as a descriptive string and an indication if the torrent was stopped/paused |
|
if (state.equals("stopped")) { |
|
return TorrentStatus.Paused; |
|
} else if (state.equals("running")) { |
|
return TorrentStatus.Downloading; |
|
} else if (state.equals("hashing")) { |
|
return TorrentStatus.Checking; |
|
} else if (state.equals("queued")) { |
|
return TorrentStatus.Queued; |
|
} |
|
return TorrentStatus.Unknown; |
|
} |
|
|
|
@Override |
|
public Daemon getType() { |
|
return settings.getType(); |
|
} |
|
|
|
@Override |
|
public DaemonSettings getSettings() { |
|
return this.settings; |
|
} |
|
|
|
}
|
|
|