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 c16d05bd..68ed6b57 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt @@ -5,6 +5,7 @@ import io.reactivex.Flowable import io.reactivex.Single import org.transdroid.connect.model.Torrent import org.transdroid.connect.model.TorrentDetails +import org.transdroid.connect.model.TorrentFile import java.io.InputStream /** @@ -25,6 +26,12 @@ internal class ClientDelegate(private val client: Client, private val actual: An throw UnsupportedFeatureException(client, Feature.LISTING) } + override fun files(torrent: Torrent): Flowable { + if (client.supports(Feature.LISTING)) + return (actual as Feature.Listing).files(torrent) + throw UnsupportedFeatureException(client, Feature.LISTING) + } + override fun details(torrent: Torrent): Single { if (client.supports(Feature.DETAILS)) return (actual as Feature.Details).details(torrent) 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 a6793f90..95a13cdc 100644 --- a/connect/src/main/java/org/transdroid/connect/clients/Feature.kt +++ b/connect/src/main/java/org/transdroid/connect/clients/Feature.kt @@ -5,6 +5,7 @@ import io.reactivex.Flowable import io.reactivex.Single import org.transdroid.connect.model.Torrent import org.transdroid.connect.model.TorrentDetails +import org.transdroid.connect.model.TorrentFile import java.io.InputStream import kotlin.reflect.KClass @@ -33,6 +34,8 @@ enum class Feature constructor(val type: KClass<*>) { fun torrents(): Flowable + fun files(torrent: Torrent): Flowable + } interface Details { diff --git a/connect/src/main/java/org/transdroid/connect/clients/rtorrent/FileSpec.kt b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/FileSpec.kt new file mode 100644 index 00000000..afedb7d0 --- /dev/null +++ b/connect/src/main/java/org/transdroid/connect/clients/rtorrent/FileSpec.kt @@ -0,0 +1,9 @@ +package org.transdroid.connect.clients.rtorrent + +data class FileSpec( + val pathName: String, + var size: Long, + var chunksDone: Long, + var chunksTotal: Long, + var priority: Long, + var pathFull: String) \ No newline at end of file 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 3f6d4d09..bc91b437 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 @@ -3,12 +3,11 @@ package org.transdroid.connect.clients.rtorrent import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Single +import io.reactivex.functions.BiFunction 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.model.* import org.transdroid.connect.util.OkHttpBuilder import org.transdroid.connect.util.flatten import retrofit2.Retrofit @@ -65,6 +64,16 @@ class Rtorrent(private val configuration: Configuration) : .addArrayDeserializer(TrackerSpec::class.java) { arrayValues -> TrackerSpec(arrayValues.asString(0)) } + .addArrayDeserializer(FileSpec::class.java) { arrayValues -> + FileSpec( + arrayValues.asString(0), + arrayValues.asLong(1), + arrayValues.asLong(2), + arrayValues.asLong(3), + arrayValues.asLong(4), + arrayValues.asString(5) + ) + } .create()) .build().create(Service::class.java) @@ -134,6 +143,30 @@ class Rtorrent(private val configuration: Configuration) : } } + override fun files(torrent: Torrent): Flowable { + return service.files(configuration.endpoint, + torrent.uniqueId, + "", + "f.path=", + "f.size_bytes=", + "f.completed_chunks=", + "f.size_chunks=", + "f.priority=", + "f.frozen_path=") + .flatten() + .zipWith(Flowable.range(0, Int.MAX_VALUE), BiFunction> { file, index -> Pair(index, file) }) + .map { (index, file) -> + TorrentFile( + index.toString(), + file.pathName, + file.pathFull.substring(torrent.locationDir.orEmpty().length), + file.pathName, + file.size, + file.size * (file.chunksDone / file.chunksTotal), + fileStatus(file.priority)) + } + } + override fun details(torrent: Torrent): Single { return service.trackers(configuration.endpoint, torrent.uniqueId, "", "t.url=") .flatten() @@ -214,6 +247,13 @@ class Rtorrent(private val configuration: Configuration) : } } + private fun fileStatus(state: Long): Priority = + when (state) { + 0L -> Priority.OFF + 2L -> Priority.HIGH + else -> Priority.NORMAL + } + private fun torrentTimeAdded(timeAdded: String?, timeCreated: Long): Date = if (timeAdded.isNullOrBlank()) Date(timeCreated * 1000L) else Date(timeAdded!!.trim().toLong() * 1000L) 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 87c3dd52..c6ffa0af 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 @@ -17,6 +17,10 @@ internal interface Service { @POST("{endpoint}") fun torrents(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single> + @XmlRpc("f.multicall") + @POST("{endpoint}") + fun files(@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> diff --git a/connect/src/main/java/org/transdroid/connect/model/Priority.kt b/connect/src/main/java/org/transdroid/connect/model/Priority.kt new file mode 100644 index 00000000..8d173803 --- /dev/null +++ b/connect/src/main/java/org/transdroid/connect/model/Priority.kt @@ -0,0 +1,8 @@ +package org.transdroid.connect.model + +enum class Priority { + OFF, + LOW, + NORMAL, + HIGH +} \ 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 9c9bdcd1..236d2328 100644 --- a/connect/src/main/java/org/transdroid/connect/model/Torrent.kt +++ b/connect/src/main/java/org/transdroid/connect/model/Torrent.kt @@ -87,22 +87,13 @@ data class Torrent( fun mimicStop(): Torrent = mimicStatus(TorrentStatus.QUEUED) - fun mimicNewLabel(newLabel: String): Torrent = Torrent(id, hash, name, statusCode, locationDir, rateDownload, rateUpload, seedersConnected, - seedersKnown, leechersConnected, leechersKnown, eta, downloadedEver, uploadedEver, totalSize, partDone, available, newLabel, dateAdded, - dateDone, error) - - fun mimicChecking(): Torrent { - return mimicStatus(TorrentStatus.CHECKING) - } - - fun mimicNewLocation(newLocation: String): Torrent { - return Torrent(id, hash, name, statusCode, newLocation, rateDownload, rateUpload, seedersConnected, seedersKnown, leechersConnected, - leechersKnown, eta, downloadedEver, uploadedEver, totalSize, partDone, available, label, dateAdded, dateDone, error) - } - - private fun mimicStatus(newStatus: TorrentStatus): Torrent = Torrent(id, hash, name, newStatus, locationDir, rateDownload, rateUpload, - seedersConnected, seedersKnown, leechersConnected, leechersKnown, eta, downloadedEver, uploadedEver, totalSize, partDone, available, - label, dateAdded, dateDone, error) + fun mimicChecking(): Torrent = mimicStatus(TorrentStatus.CHECKING) + + fun mimicNewLabel(newLabel: String): Torrent = this.copy(label = newLabel) + + fun mimicNewLocation(newLocation: String): Torrent = this.copy(locationDir = newLocation) + + private fun mimicStatus(newStatus: TorrentStatus): Torrent = this.copy(statusCode = newStatus) override fun toString(): String = "($uniqueId) $name" diff --git a/connect/src/main/java/org/transdroid/connect/model/TorrentFile.kt b/connect/src/main/java/org/transdroid/connect/model/TorrentFile.kt new file mode 100644 index 00000000..82fb367f --- /dev/null +++ b/connect/src/main/java/org/transdroid/connect/model/TorrentFile.kt @@ -0,0 +1,14 @@ +package org.transdroid.connect.model + +data class TorrentFile( + val key: String, + val name: String, + val relativePath: String, + val fullPath: String, + val totalSize: Long, + val downloaded: Long, + val priority: Priority) { + + fun mimicPriority(newPriority: Priority): TorrentFile = this.copy(priority = newPriority) + +} \ No newline at end of file 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 7b313678..3d0fc063 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 @@ -43,18 +43,26 @@ class RtorrentLiveTest { } @Test - fun details() { - rtorrent.details(firstLiveTorrent()) + fun torrents() { + rtorrent.torrents() + .toList() .test() - .assertValue { it.trackers.isNotEmpty() } + .assertValue { it.size > 0 } } @Test - fun torrents() { - rtorrent.torrents() + fun files() { + rtorrent.files(firstLiveTorrent()) .toList() .test() - .assertValue { torrents -> torrents.size > 0 } + .assertValue { it.size > 0 } + } + + @Test + fun details() { + rtorrent.details(firstLiveTorrent()) + .test() + .assertValue { it.trackers.isNotEmpty() } } @Test 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 13877db9..74612b25 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,6 +7,7 @@ import org.junit.Test import org.transdroid.connect.Configuration import org.transdroid.connect.clients.Client import org.transdroid.connect.mock.MockTorrent +import org.transdroid.connect.model.Priority class RtorrentMockTest { @@ -37,6 +38,23 @@ class RtorrentMockTest { server.takeRequest() } + @Test + fun files() { + server.enqueue(mock("ubuntu-17.04-desktop-amd64.iso1609039872030691/downloads/ubuntu-17.04-desktop-amd64.iso")) + rtorrent.files(MockTorrent.downloading) + .test() + .assertValue { + it.key == "0" && + it.name == "ubuntu-17.04-desktop-amd64.iso" && + it.relativePath == "ubuntu-17.04-desktop-amd64.iso" && + it.fullPath == "ubuntu-17.04-desktop-amd64.iso" && + it.totalSize == 1609039872L && + it.downloaded == 0L && + it.priority == Priority.NORMAL + } + server.takeRequest() + } + @Test fun details() { server.enqueue(mock("http://torrent.ubuntu.com:6969/announcehttp://ipv6.torrent.ubuntu.com:6969/announce"))