Browse Source

Added BitComet support, courtesy of Sergey (SeNS). Thanks\!

pull/11/head
Eric Kok 12 years ago
parent
commit
658f72cce0
  1. 1
      .hgignore
  2. 2
      android/res/values/arrays.xml
  3. 238
      lib/src/com/android/internalcopy/http/multipart/BitCometFilePart.java
  4. 43
      lib/src/com/android/internalcopy/http/multipart/Utf8StringPart.java
  5. 536
      lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java
  6. 18
      lib/src/org/transdroid/daemon/Daemon.java

1
.hgignore

@ -5,3 +5,4 @@ syntax: glob @@ -5,3 +5,4 @@ syntax: glob
.metadata/
bin/
gen/
lint.xml

2
android/res/values/arrays.xml

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
<resources>
<string-array name="pref_daemon_types">
<item>BitComet</item>
<item>Bitflu 1.2+</item>
<item>BitTorrent 6+</item>
<item>Buffalo NAS -1.31</item>
@ -17,6 +18,7 @@ @@ -17,6 +18,7 @@
<item>Vuze</item>
</string-array>
<string-array name="pref_daemon_values">
<item>daemon_bitcomet</item>
<item>daemon_bitflu</item>
<item>daemon_bittorrent</item>
<item>daemon_buffalonas</item>

238
lib/src/com/android/internalcopy/http/multipart/BitCometFilePart.java

@ -0,0 +1,238 @@ @@ -0,0 +1,238 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v 1.19 2004/04/18 23:51:37 jsdever Exp $
* $Revision: 480424 $
* $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package com.android.internalcopy.http.multipart;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.http.util.EncodingUtils;
/**
* This class implements a part of a Multipart post object that
* consists of a file.
*
* @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
* @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
* @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
* @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
* @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*
* @since 2.0
*
*/
public class BitCometFilePart extends PartBase {
/** Default content encoding of file attachments. */
public static final String DEFAULT_CONTENT_TYPE = "application/x-bittorrent";
/** Attachment's file name */
protected static final String FILE_NAME = "; filename=";
/** Attachment's file name as a byte array */
private static final byte[] FILE_NAME_BYTES =
EncodingUtils.getAsciiBytes(FILE_NAME);
/** Source of the file part. */
private PartSource source;
/**
* FilePart Constructor.
*
* @param name the name for this part
* @param partSource the source for this part
* @param contentType the content type for this part, if <code>null</code> the
* {@link #DEFAULT_CONTENT_TYPE default} is used
* @param charset the charset encoding for this part, if <code>null</code> the
* {@link #DEFAULT_CHARSET default} is used
*/
public BitCometFilePart(String name, PartSource partSource, String contentType, String charset) {
super(
name,
contentType == null ? DEFAULT_CONTENT_TYPE : contentType,
charset == null ? "ISO-8859-1" : charset,
null
);
if (partSource == null) {
throw new IllegalArgumentException("Source may not be null");
}
this.source = partSource;
}
/**
* FilePart Constructor.
*
* @param name the name for this part
* @param partSource the source for this part
*/
public BitCometFilePart(String name, PartSource partSource) {
this(name, partSource, null, null);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param file the file to post
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public BitCometFilePart(String name, File file)
throws FileNotFoundException {
this(name, new FilePartSource(file), null, null);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param file the file to post
* @param contentType the content type for this part, if <code>null</code> the
* {@link #DEFAULT_CONTENT_TYPE default} is used
* @param charset the charset encoding for this part, if <code>null</code> the
* {@link #DEFAULT_CHARSET default} is used
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public BitCometFilePart(String name, File file, String contentType, String charset)
throws FileNotFoundException {
this(name, new FilePartSource(file), contentType, charset);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param fileName the file name
* @param file the file to post
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public BitCometFilePart(String name, String fileName, File file)
throws FileNotFoundException {
this(name, new FilePartSource(fileName, file), null, null);
}
/**
* FilePart Constructor.
*
* @param name the name of the file part
* @param fileName the file name
* @param file the file to post
* @param contentType the content type for this part, if <code>null</code> the
* {@link #DEFAULT_CONTENT_TYPE default} is used
* @param charset the charset encoding for this part, if <code>null</code> the
* {@link #DEFAULT_CHARSET default} is used
*
* @throws FileNotFoundException if the <i>file</i> is not a normal
* file or if it is not readable.
*/
public BitCometFilePart(String name, String fileName, File file, String contentType, String charset)
throws FileNotFoundException {
this(name, new FilePartSource(fileName, file), contentType, charset);
}
/**
* Write the disposition header to the output stream
* @param out The output stream
* @throws IOException If an IO problem occurs
* @see Part#sendDispositionHeader(OutputStream)
*/
@Override
protected void sendDispositionHeader(OutputStream out)
throws IOException {
super.sendDispositionHeader(out);
String filename = this.source.getFileName();
if (filename != null) {
out.write(FILE_NAME_BYTES);
out.write(QUOTE_BYTES);
out.write(EncodingUtils.getAsciiBytes(filename));
out.write(QUOTE_BYTES);
}
}
/**
* Write the data in "source" to the specified stream.
* @param out The output stream.
* @throws IOException if an IO problem occurs.
* @see Part#sendData(OutputStream)
*/
@Override
protected void sendData(OutputStream out) throws IOException {
if (lengthOfData() == 0) {
// this file contains no data, so there is nothing to send.
// we don't want to create a zero length buffer as this will
// cause an infinite loop when reading.
return;
}
byte[] tmp = new byte[4096];
InputStream instream = source.createInputStream();
try {
int len;
while ((len = instream.read(tmp)) >= 0) {
out.write(tmp, 0, len);
}
} finally {
// we're done with the stream, close it
instream.close();
}
}
/**
* Returns the source of the file part.
*
* @return The source.
*/
protected PartSource getSource() {
return this.source;
}
/**
* Return the length of the data.
* @return The length.
* @see Part#lengthOfData()
*/
@Override
protected long lengthOfData() {
return source.getLength();
}
}

43
lib/src/com/android/internalcopy/http/multipart/Utf8StringPart.java

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
package com.android.internalcopy.http.multipart;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.http.util.EncodingUtils;
public class Utf8StringPart extends PartBase {
/** Contents of this StringPart. */
private byte[] content;
/** The String value of this part. */
private String value;
public Utf8StringPart(String name, String value) {
super(name, null, null, null);
this.value = value;
}
/**
* Gets the content in bytes. Bytes are lazily created to allow the charset to be changed
* after the part is created.
*
* @return the content in bytes
*/
private byte[] getContent() {
if (content == null) {
content = EncodingUtils.getBytes(value, "utf-8");
}
return content;
}
@Override
protected void sendData(OutputStream out) throws IOException {
out.write(getContent());
}
@Override
protected long lengthOfData() throws IOException {
return getContent().length;
}
}

536
lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java

@ -0,0 +1,536 @@ @@ -0,0 +1,536 @@
/*
* 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.BitComet;
import java.io.File;
import java.io.FileNotFoundException;
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;
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.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
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.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
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.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.Part;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.BitCometFilePart;
import com.android.internalcopy.http.multipart.Utf8StringPart;
/**
* The daemon adapter for the BitComet torrent client.
*
* @author SeNS (sensboston)
*
*/
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;
}
@Override
public DaemonTaskResult executeTask(DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
String result = makeRequest("/panel/task_list");
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseHttpTorrents(result), null);
case GetFileList:
// Request files listing for a specific torrent
String fhash = ((GetFileListTask)task).getTargetTorrent().getUniqueID();
result = makeRequest("/panel/task_detail", new BasicNameValuePair("id", fhash), new BasicNameValuePair("show", "files"));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseHttpTorrentFiles(result, fhash));
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask)task).getFile();
makeFileUploadRequest("/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("/panel/task_add_httpftp_result", url);
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a torrent by URL
String magnetUrl = ((AddByMagnetUrlTask)task).getUrl();
makeUploadUrlRequest("/panel/task_add_httpftp_result", magnetUrl);
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest("/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("/panel/task_action", new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()), new BasicNameValuePair("action", "stop"));
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeRequest("/panel/task_action", new BasicNameValuePair("id", task.getTargetTorrent().getUniqueID()), new BasicNameValuePair("action", "start"));
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.
* @param connectionTimeout The connection timeout in milliseconds
* @throws DaemonException On conflicting or missing settings
*/
private void initialise(int connectionTimeout) throws DaemonException {
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(String url, NameValuePair... params) throws DaemonException {
try {
// Initialize the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
}
// Add the parameters to the query string
boolean first = true;
for (NameValuePair param : params) {
if (first) {
url += "?";
first = false;
} else {
url += "&";
}
url += param.getName() + "=" + param.getValue();
}
// Make the request
HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl(url)));
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read JSON response
java.io.InputStream instream = entity.getContent();
String result = HttpHelper.ConvertStreamToString(instream);
instream.close();
// Return raw result
return result;
}
DLog.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) {
DLog.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeFileUploadRequest(String path, String file) throws DaemonException {
try {
// Initialize the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
}
// 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) {
DLog.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private boolean makeUploadUrlRequest(String path, String url) throws DaemonException {
try {
// Initialize the HTTP client
if (httpclient == null) {
initialise(HttpHelper.DEFAULT_CONNECTION_TIMEOUT);
}
// 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<NameValuePair>();
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) {
DLog.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Parse BitComet HTML page (http response)
* @param response
* @return
* @throws DaemonException
*/
private ArrayList<Torrent> parseHttpTorrents(String response) throws DaemonException {
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
try {
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 = parseStatus(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 = 0;
String comment = "";
Date dateAdded = new Date();
// Comment code below to speedup torrent listing
// P.S. feature request to extend torrents info is already sent to the BitComet developers
//*
try {
// Lets make summary request and parse details
String summary = makeRequest("/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(" (")));
}
catch (Exception e) {}
//*
// Add the parsed torrent to the list
torrents.add(new Torrent(
(long)i-2,
null,
name,
status,
null,
rateDown,
rateUp,
leechers,
seeders,
knownLeechers,
knownSeeders,
(rateDown == 0? -1: (int) ((size - sizeDone) / rateDown)),
sizeDone,
sizeUp,
size,
percentDone / 100,
distributed_copies,
comment,
dateAdded,
null));
}
}
}
catch (Exception e) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response.");
}
return torrents;
}
/**
* Parse BitComet HTML page (http response)
* @param response
* @return
* @throws DaemonException
*/
private ArrayList<TorrentFile> parseHttpTorrentFiles(String response, String hash) throws DaemonException {
// Parse response
ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>();
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("&nbsp&nbsp ")+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);
}
torrentfiles.add(new TorrentFile(
hash,
fileDetails[3],
fileDetails[3],
settings.getDownloadDir() + fileDetails[3],
size,
sizeDone,
parsePriority(fileDetails[1])));
}
}
catch (Exception e) {
throw new DaemonException(ExceptionType.UnexpectedResponse, "Invalid BitComet HTTP response.");
}
// Return the list
return torrentfiles;
}
/**
* 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 number of kB
*/
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 e) { }
return 0;
}
/**
* Parse BitComet torrent files priority
**/
private Priority parsePriority(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 parseStatus(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;
}
return TorrentStatus.Unknown;
}
@Override
public Daemon getType() {
return settings.getType();
}
@Override
public DaemonSettings getSettings() {
return this.settings;
}
}

18
lib/src/org/transdroid/daemon/Daemon.java

@ -28,6 +28,7 @@ import org.transdroid.daemon.Utorrent.UtorrentAdapter; @@ -28,6 +28,7 @@ import org.transdroid.daemon.Utorrent.UtorrentAdapter;
import org.transdroid.daemon.Vuze.VuzeAdapter;
import org.transdroid.daemon.Bitflu.BitfluAdapter;
import org.transdroid.daemon.BuffaloNas.BuffaloNasAdapter;
import org.transdroid.daemon.BitComet.BitCometAdapter;
/**
* Factory for new instances of server daemons, based on user settings.
@ -96,6 +97,11 @@ public enum Daemon { @@ -96,6 +97,11 @@ public enum Daemon {
public IDaemonAdapter createAdapter(DaemonSettings settings) {
return new VuzeAdapter(settings);
}
},
BitComet {
public IDaemonAdapter createAdapter(DaemonSettings settings) {
return new BitCometAdapter(settings);
}
};
public abstract IDaemonAdapter createAdapter(DaemonSettings settings);
@ -145,6 +151,9 @@ public enum Daemon { @@ -145,6 +151,9 @@ public enum Daemon {
if (daemonCode.equals("daemon_vuze")) {
return Vuze;
}
if (daemonCode.equals("daemon_bitcomet")) {
return BitComet;
}
return null;
}
@ -162,6 +171,7 @@ public enum Daemon { @@ -162,6 +171,7 @@ public enum Daemon {
case DLinkRouterBT:
case rTorrent:
case Tfb4rt:
case BitComet:
if (ssl) {
return 443;
} else {
@ -188,7 +198,7 @@ public enum Daemon { @@ -188,7 +198,7 @@ public enum Daemon {
}
public static boolean supportsFileListing(Daemon type) {
return type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas;
return type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet;
}
public static boolean supportsFineDetails(Daemon type) {
@ -225,11 +235,11 @@ public enum Daemon { @@ -225,11 +235,11 @@ public enum Daemon {
}
public static boolean supportsAddByMagnetUrl(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Transmission || type == Deluge || type == Bitflu || type == KTorrent || type == rTorrent || type == qBittorrent;
return type == uTorrent || type == BitTorrent || type == Transmission || type == Deluge || type == Bitflu || type == KTorrent || type == rTorrent || type == qBittorrent || type == BitComet;
}
public static boolean supportsRemoveWithData(Daemon type) {
return type == uTorrent || type == Vuze || type == Transmission || type == Deluge || type == BitTorrent || type == Tfb4rt || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas;
return type == uTorrent || type == Vuze || type == Transmission || type == Deluge || type == BitTorrent || type == Tfb4rt || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet;
}
public static boolean supportsFilePrioritySetting(Daemon type) {
@ -241,7 +251,7 @@ public enum Daemon { @@ -241,7 +251,7 @@ public enum Daemon {
}
public static boolean supportsLabels(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Deluge; // || type == Vuze
return type == uTorrent || type == BitTorrent || type == Deluge || type == BitComet; // || type == Vuze
}
public static boolean supportsSetLabel(Daemon type) {

Loading…
Cancel
Save