diff --git a/connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt b/connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt index e30260db..c16d05bd 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt @@ -4,6 +4,7 @@ import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Single import org.transdroid.connect.model.Torrent +import org.transdroid.connect.model.TorrentDetails import java.io.InputStream /** @@ -12,16 +13,22 @@ import java.io.InputStream */ internal class ClientDelegate(private val client: Client, private val actual: Any) : ClientSpec { + override fun clientVersion(): Single { + if (client.supports(Feature.VERSION)) + return (actual as Feature.Version).clientVersion() + throw UnsupportedFeatureException(client, Feature.VERSION) + } + override fun torrents(): Flowable { if (client.supports(Feature.LISTING)) return (actual as Feature.Listing).torrents() throw UnsupportedFeatureException(client, Feature.LISTING) } - override fun clientVersion(): Single { - if (client.supports(Feature.VERSION)) - return (actual as Feature.Version).clientVersion() - throw UnsupportedFeatureException(client, Feature.VERSION) + override fun details(torrent: Torrent): Single { + if (client.supports(Feature.DETAILS)) + return (actual as Feature.Details).details(torrent) + throw UnsupportedFeatureException(client, Feature.DETAILS) } override fun resume(torrent: Torrent): Single { diff --git a/connect/src/main/java/org/transdroid/connect/clients/ClientSpec.kt b/connect/src/main/java/org/transdroid/connect/clients/ClientSpec.kt index 905db401..5133b6fb 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/ClientSpec.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/ClientSpec.kt @@ -3,6 +3,7 @@ package org.transdroid.connect.clients interface ClientSpec : Feature.Version, Feature.Listing, + Feature.Details, Feature.StartingStopping, Feature.ResumingPausing, Feature.ForceStarting, diff --git a/connect/src/main/java/org/transdroid/connect/clients/Feature.kt b/connect/src/main/java/org/transdroid/connect/clients/Feature.kt index 964de2ad..a6793f90 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/Feature.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/Feature.kt @@ -4,6 +4,7 @@ import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Single import org.transdroid.connect.model.Torrent +import org.transdroid.connect.model.TorrentDetails import java.io.InputStream import kotlin.reflect.KClass @@ -14,6 +15,7 @@ enum class Feature constructor(val type: KClass<*>) { VERSION(Version::class), LISTING(Listing::class), + DETAILS(Details::class), STARTING_STOPPING(StartingStopping::class), RESUMING_PAUSING(ResumingPausing::class), FORCE_STARTING(ForceStarting::class), @@ -33,6 +35,12 @@ enum class Feature constructor(val type: KClass<*>) { } + interface Details { + + fun details(torrent: Torrent): Single + + } + interface StartingStopping { fun start(torrent: Torrent): Single diff --git a/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Rtorrent.kt b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Rtorrent.kt index a883fef1..3f6d4d09 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Rtorrent.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Rtorrent.kt @@ -7,6 +7,7 @@ import nl.nl2312.xmlrpc.XmlRpcConverterFactory import org.transdroid.connect.Configuration import org.transdroid.connect.clients.Feature import org.transdroid.connect.model.Torrent +import org.transdroid.connect.model.TorrentDetails import org.transdroid.connect.model.TorrentStatus import org.transdroid.connect.util.OkHttpBuilder import org.transdroid.connect.util.flatten @@ -18,6 +19,7 @@ import java.util.* class Rtorrent(private val configuration: Configuration) : Feature.Version, Feature.Listing, + Feature.Details, Feature.StartingStopping, Feature.ResumingPausing, Feature.AddByFile, @@ -60,6 +62,9 @@ class Rtorrent(private val configuration: Configuration) : ) } + .addArrayDeserializer(TrackerSpec::class.java) { arrayValues -> + TrackerSpec(arrayValues.asString(0)) + } .create()) .build().create(Service::class.java) @@ -123,11 +128,20 @@ class Rtorrent(private val configuration: Configuration) : null, label, torrentTimeAdded(timeAdded, timeCreated), - torrentTimeFinished(timeFinished), errorMessage + torrentTimeFinished(timeFinished), + errorMessage ) } } + override fun details(torrent: Torrent): Single { + return service.trackers(configuration.endpoint, torrent.uniqueId, "", "t.url=") + .flatten() + .map { (url) -> url } + .toList() + .map { trackers -> TorrentDetails(trackers, listOfNotNull(torrent.error)) } + } + override fun start(torrent: Torrent): Single { return service.open(configuration.endpoint, torrent.uniqueId) .flatMap { service.start(configuration.endpoint, torrent.uniqueId) } diff --git a/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Service.kt b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Service.kt index 66674eec..87c3dd52 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Service.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/Service.kt @@ -1,7 +1,5 @@ package org.transdroid.connect.clients.rtorrent -import io.reactivex.Completable -import io.reactivex.Flowable import io.reactivex.Single import nl.nl2312.xmlrpc.Nothing import nl.nl2312.xmlrpc.XmlRpc @@ -17,7 +15,11 @@ internal interface Service { @XmlRpc("d.multicall2") @POST("{endpoint}") - fun torrents(@Path("endpoint") endpoint: String?, @Body vararg args: String): Flowable> + fun torrents(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single> + + @XmlRpc("t.multicall") + @POST("{endpoint}") + fun trackers(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single> @XmlRpc("d.start") @POST("{endpoint}") diff --git a/connect/src/main/java/org/transdroid/connect/clients/rtorrent/TrackerSpec.kt b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/TrackerSpec.kt new file mode 100644 index 00000000..0ffd4177 --- /dev/null +++ b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/TrackerSpec.kt @@ -0,0 +1,3 @@ +package org.transdroid.connect.clients.rtorrent + +data class TrackerSpec(val url: String) \ No newline at end of file diff --git a/connect/src/main/java/org/transdroid/connect/model/Torrent.kt b/connect/src/main/java/org/transdroid/connect/model/Torrent.kt index 2f6fbdf9..9c9bdcd1 100644 --- a/connect/src/main/java/org/transdroid/connect/model/Torrent.kt +++ b/connect/src/main/java/org/transdroid/connect/model/Torrent.kt @@ -22,31 +22,18 @@ data class Torrent( val available: Float?, val label: String?, val dateAdded: Date, - val realDateDone: Date?, + private val realDateDone: Date?, val error: String?) { - val dateDone: Date - - init { - if (realDateDone != null) { - this.dateDone = realDateDone - } else { - if (this.partDone == 1f) { - // Finished but no finished date: set so move to bottom of list - this.dateDone = Calendar.getInstance().apply { - clear() - set(1900, Calendar.DECEMBER, 31) - }.time - } else if (eta == null || eta == -1L || eta == -2L) { - // Unknown eta: move to the top of the list - this.dateDone = Date(java.lang.Long.MAX_VALUE) - } else { - this.dateDone = Calendar.getInstance().apply { - add(Calendar.SECOND, eta.toInt()) - }.time - } - } - } + val dateDone: Date = realDateDone ?: + if (this.partDone == 1f) Calendar.getInstance().apply { + clear() + set(1900, Calendar.DECEMBER, 31) + }.time + else if (eta == null || eta == -1L || eta == -2L) Date(java.lang.Long.MAX_VALUE) + else Calendar.getInstance().apply { + add(Calendar.SECOND, eta.toInt()) + }.time /** * The unique torrent-specific id, which is the torrent's hash or (if not available) the local index number diff --git a/connect/src/main/java/org/transdroid/connect/model/TorrentDetails.kt b/connect/src/main/java/org/transdroid/connect/model/TorrentDetails.kt new file mode 100644 index 00000000..165828d8 --- /dev/null +++ b/connect/src/main/java/org/transdroid/connect/model/TorrentDetails.kt @@ -0,0 +1,5 @@ +package org.transdroid.connect.model + +data class TorrentDetails( + val trackers: List, + var errors: List) \ No newline at end of file diff --git a/connect/src/main/java/org/transdroid/connect/util/RxUtil.kt b/connect/src/main/java/org/transdroid/connect/util/RxUtil.kt index 25e8cbba..98eb6325 100644 --- a/connect/src/main/java/org/transdroid/connect/util/RxUtil.kt +++ b/connect/src/main/java/org/transdroid/connect/util/RxUtil.kt @@ -1,5 +1,6 @@ package org.transdroid.connect.util import io.reactivex.Flowable +import io.reactivex.Single -fun Flowable>.flatten(): Flowable = this.flatMapIterable { items -> items.toList() } +fun Single>.flatten(): Flowable = this.flattenAsFlowable { items -> items.toList() } diff --git a/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentLiveTest.kt b/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentLiveTest.kt index b622e17f..7b313678 100644 --- a/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentLiveTest.kt +++ b/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentLiveTest.kt @@ -1,6 +1,5 @@ package org.transdroid.connect.clients.rtorrent -import com.burgstaller.okhttp.digest.Credentials import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -43,6 +42,13 @@ class RtorrentLiveTest { .assertValue("0.9.6") } + @Test + fun details() { + rtorrent.details(firstLiveTorrent()) + .test() + .assertValue { it.trackers.isNotEmpty() } + } + @Test fun torrents() { rtorrent.torrents() diff --git a/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentMockTest.kt b/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentMockTest.kt index 002a6520..13877db9 100644 --- a/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentMockTest.kt +++ b/connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentMockTest.kt @@ -7,7 +7,6 @@ import org.junit.Test import org.transdroid.connect.Configuration import org.transdroid.connect.clients.Client import org.transdroid.connect.mock.MockTorrent -import java.io.File class RtorrentMockTest { @@ -38,6 +37,21 @@ class RtorrentMockTest { server.takeRequest() } + @Test + fun details() { + server.enqueue(mock("http://torrent.ubuntu.com:6969/announcehttp://ipv6.torrent.ubuntu.com:6969/announce")) + rtorrent.details(MockTorrent.error) + .test() + .assertValue { + it.trackers.size == 2 && + it.trackers[0] == "http://torrent.ubuntu.com:6969/announce" && + it.trackers[1] == "http://ipv6.torrent.ubuntu.com:6969/announce" && + it.errors.size == 1 && + it.errors[0] == "tracker error" + } + server.takeRequest() + } + @Test fun addByUrl() { server.enqueue(mock("0.9.6")) diff --git a/connect/src/test/java/org/transdroid/connect/mock/MockTorrent.kt b/connect/src/test/java/org/transdroid/connect/mock/MockTorrent.kt index f5ec22f2..5952a695 100644 --- a/connect/src/test/java/org/transdroid/connect/mock/MockTorrent.kt +++ b/connect/src/test/java/org/transdroid/connect/mock/MockTorrent.kt @@ -12,8 +12,13 @@ object MockTorrent { val torrentFile = File("connect/src/test/resources/test/ubuntu.torrent").inputStream() val downloading = Torrent(0, "59066769B9AD42DA2E508611C33D7C4480B3857B", "ubuntu-17.04-desktop-amd64.iso", TorrentStatus.DOWNLOADING, - "/downloads/", 1000000, 200000, 20, 20, 2, 50, null, 804519936, 160903987, 1609039872, 0.5F, 0.8F, "distros", Date(1492681983), null, null) + "/downloads/", 1000000, 200000, 20, 20, 2, 50, null, 804519936, 160903987, 1609039872, 0.5F, 0.8F, "distros", Date(1492681983), + null, null) val seeding = Torrent(0, "59066769B9AD42DA2E508611C33D7C4480B3857B", "ubuntu-17.04-desktop-amd64.iso", TorrentStatus.SEEDING, - "/downloads/", 0, 1000000, 0, 24, 10, 50, null, 1609039872, 2609039872, 1609039872, 1F, 1F, "distros", Date(1492681983), Date(1492781983), null) + "/downloads/", 0, 1000000, 0, 24, 10, 50, null, 1609039872, 2609039872, 1609039872, 1F, 1F, "distros", Date(1492681983), + Date(1492781983), null) + val error = Torrent(0, "59066769B9AD42DA2E508611C33D7C4480B3857B", "ubuntu-17.04-desktop-amd64.iso", TorrentStatus.ERROR, + "/downloads/", 0, 1000000, 0, 0, 0, 0, null, 1609039872, 2609039872, 1609039872, 1F, 1F, "distros", Date(1492681983), + Date(1492781983), "tracker error") } \ No newline at end of file