From e903343a80883000e36afe1e468c4036662dbb44 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Mon, 1 Oct 2012 17:25:26 +0200 Subject: [PATCH] Applied Sergey's patch for improved (XML instead of HTML parsing) based BitCOmet support. --- .../daemon/BitComet/BitCometAdapter.java | 223 +++++++++++++++++- lib/src/org/transdroid/daemon/Daemon.java | 6 +- 2 files changed, 216 insertions(+), 13 deletions(-) diff --git a/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java b/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java index bc35ee80..31d25583 100644 --- a/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java +++ b/lib/src/org/transdroid/daemon/BitComet/BitCometAdapter.java @@ -19,6 +19,7 @@ package org.transdroid.daemon.BitComet; import java.io.File; import java.io.FileNotFoundException; +import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.text.DateFormat; @@ -58,6 +59,7 @@ 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.DLog; import org.transdroid.daemon.util.HttpHelper; @@ -66,11 +68,18 @@ import com.android.internalcopy.http.multipart.MultipartEntity; import com.android.internalcopy.http.multipart.BitCometFilePart; import com.android.internalcopy.http.multipart.Utf8StringPart; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + + /** * The daemon adapter for the BitComet torrent client. * * @author SeNS (sensboston) * + * 09/26/2012: added AJAX support for BitComet v.1.34 and up + * : added additional tasks support */ public class BitCometAdapter implements IDaemonAdapter { @@ -91,7 +100,13 @@ public class BitCometAdapter implements IDaemonAdapter { case Retrieve: // Request all torrents from server - String result = makeRequest("/panel/task_list"); + // first, check client for the new AJAX interface (BitComet v.1.34 and up) + String result = makeRequest("/panel/task_list_xml"); + if (result.startsWith("")).replaceAll("", "").replaceAll("", "").replaceAll("\n", "").split(""); for (int i=2; i")+1, name.indexOf("<")); - TorrentStatus status = parseStatus(subParts[3]); + TorrentStatus status = convertStatus(subParts[3]); String percenDoneStr = subParts[6]; String downloadRateStr = subParts[7]; String uploadRateStr = subParts[8]; @@ -434,7 +486,149 @@ public class BitCometAdapter implements IDaemonAdapter { } /** - * Parse BitComet HTML page (http response) + * Parse BitComet AJAX response + * that code was copy-pasted and slightly modified from \Ktorrent\StatsParser.java + * @param response + * @return + * @throws DaemonException + */ + private ArrayList parseXmlTorrents(String response) throws DaemonException { + + ArrayList 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); + torrents.add(new Torrent( + id, + null, // hash, // we suppose to use simple integer IDs + name, + status, + null, + rateDown, + rateUp, + leechers, + seeders, + seeders + leechers, + seedersTotal + 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)); // Not supported in the web interface + + id++; // Stop/start/etc. requests are made by ID, which is the order number in the returned XML list :-S + + } 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. 'BT' + next = xpp.next(); + if (next == XmlPullParser.TEXT) { + if (tagName.equals("name")) { + name = xpp.getText().trim(); + } else if (tagName.equals("infohash")) { + hash = xpp.getText().trim(); + } else if (tagName.equals("state")) { + status = convertStatus(xpp.getText()); + } else if (tagName.equals("bytes_downloaded")) { + sizeDone = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("bytes_uploaded")) { + sizeUp = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("size")) { + totalSize = Long.parseLong(xpp.getText()); + } else if (tagName.equals("down_speed")) { + rateDown = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("up_speed")) { + rateUp = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("seeders")) { + seeders = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("total_seeders")) { + seedersTotal = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("peers")) { + leechers = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("total_peers")) { + leechersTotal = Integer.parseInt(xpp.getText()); + } else if (tagName.equals("progress_permillage")) { + progress = convertProgress(xpp.getText()); + } else if (tagName.equals("created_time")) { + dateAdded = new Date(Long.parseLong(xpp.getText())); + } else if (tagName.equals("comment")) { + label = xpp.getText().trim(); + } + } + } + + 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 * @return * @throws DaemonException @@ -466,7 +660,7 @@ public class BitCometAdapter implements IDaemonAdapter { settings.getDownloadDir() + fileDetails[3], size, sizeDone, - parsePriority(fileDetails[1]))); + convertPriority(fileDetails[1]))); } } catch (Exception e) { @@ -480,7 +674,7 @@ public class BitCometAdapter implements IDaemonAdapter { /** * 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 + * @return The size in bytes */ private static long convertSize(String size) { try { @@ -501,7 +695,7 @@ public class BitCometAdapter implements IDaemonAdapter { /** * Parse BitComet torrent files priority **/ - private Priority parsePriority(String priority) { + private Priority convertPriority(String priority) { if (priority.equals("Very High") || priority.equals("High")) { return Priority.High; } else if (priority.equals("Normal")) { @@ -513,7 +707,7 @@ public class BitCometAdapter implements IDaemonAdapter { /** * Parse BitComet torrent status **/ - private TorrentStatus parseStatus(String state) { + 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; @@ -522,7 +716,16 @@ public class BitCometAdapter implements IDaemonAdapter { } return TorrentStatus.Unknown; } - + + /** + * 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 Daemon getType() { return settings.getType(); diff --git a/lib/src/org/transdroid/daemon/Daemon.java b/lib/src/org/transdroid/daemon/Daemon.java index 0aa81fd0..519f3b18 100644 --- a/lib/src/org/transdroid/daemon/Daemon.java +++ b/lib/src/org/transdroid/daemon/Daemon.java @@ -214,7 +214,7 @@ public enum Daemon { } public static boolean supportsStoppingStarting(Daemon type) { - return type == uTorrent || type == rTorrent || type == BitTorrent; + return type == uTorrent || type == rTorrent || type == BitTorrent || type == BitComet; } public static boolean supportsForcedStarting(Daemon type) { @@ -226,7 +226,7 @@ public enum Daemon { } public static boolean supportsSetTransferRates(Daemon type) { - return type == Deluge || type == Transmission || type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Vuze || type == BuffaloNas; + return type == Deluge || type == Transmission || type == uTorrent || type == BitTorrent || type == Deluge || type == rTorrent || type == Vuze || type == BuffaloNas || type == BitComet; } public static boolean supportsAddByFile(Daemon type) { @@ -247,7 +247,7 @@ public enum Daemon { } public static boolean supportsDateAdded(Daemon type) { - return type == Vuze || type == Transmission || type == rTorrent || type == Bitflu; + return type == Vuze || type == Transmission || type == rTorrent || type == Bitflu || type == BitComet; } public static boolean supportsLabels(Daemon type) {