Browse Source

Added support for rTorrent file listing.

rewrite-connect
Eric Kok 8 years ago
parent
commit
8f48a3404c
  1. 7
      connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt
  2. 3
      connect/src/main/java/org/transdroid/connect/clients/Feature.kt
  3. 9
      connect/src/main/java/org/transdroid/connect/clients/rtorrent/FileSpec.kt
  4. 46
      connect/src/main/java/org/transdroid/connect/clients/rtorrent/Rtorrent.kt
  5. 4
      connect/src/main/java/org/transdroid/connect/clients/rtorrent/Service.kt
  6. 8
      connect/src/main/java/org/transdroid/connect/model/Priority.kt
  7. 23
      connect/src/main/java/org/transdroid/connect/model/Torrent.kt
  8. 14
      connect/src/main/java/org/transdroid/connect/model/TorrentFile.kt
  9. 20
      connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentLiveTest.kt
  10. 18
      connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentMockTest.kt

7
connect/src/main/java/org/transdroid/connect/clients/ClientDelegate.kt

@ -5,6 +5,7 @@ import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import org.transdroid.connect.model.Torrent import org.transdroid.connect.model.Torrent
import org.transdroid.connect.model.TorrentDetails import org.transdroid.connect.model.TorrentDetails
import org.transdroid.connect.model.TorrentFile
import java.io.InputStream import java.io.InputStream
/** /**
@ -25,6 +26,12 @@ internal class ClientDelegate(private val client: Client, private val actual: An
throw UnsupportedFeatureException(client, Feature.LISTING) throw UnsupportedFeatureException(client, Feature.LISTING)
} }
override fun files(torrent: Torrent): Flowable<TorrentFile> {
if (client.supports(Feature.LISTING))
return (actual as Feature.Listing).files(torrent)
throw UnsupportedFeatureException(client, Feature.LISTING)
}
override fun details(torrent: Torrent): Single<TorrentDetails> { override fun details(torrent: Torrent): Single<TorrentDetails> {
if (client.supports(Feature.DETAILS)) if (client.supports(Feature.DETAILS))
return (actual as Feature.Details).details(torrent) return (actual as Feature.Details).details(torrent)

3
connect/src/main/java/org/transdroid/connect/clients/Feature.kt

@ -5,6 +5,7 @@ import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import org.transdroid.connect.model.Torrent import org.transdroid.connect.model.Torrent
import org.transdroid.connect.model.TorrentDetails import org.transdroid.connect.model.TorrentDetails
import org.transdroid.connect.model.TorrentFile
import java.io.InputStream import java.io.InputStream
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -33,6 +34,8 @@ enum class Feature constructor(val type: KClass<*>) {
fun torrents(): Flowable<Torrent> fun torrents(): Flowable<Torrent>
fun files(torrent: Torrent): Flowable<TorrentFile>
} }
interface Details { interface Details {

9
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)

46
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.Completable
import io.reactivex.Flowable import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.functions.BiFunction
import nl.nl2312.xmlrpc.XmlRpcConverterFactory import nl.nl2312.xmlrpc.XmlRpcConverterFactory
import org.transdroid.connect.Configuration import org.transdroid.connect.Configuration
import org.transdroid.connect.clients.Feature import org.transdroid.connect.clients.Feature
import org.transdroid.connect.model.Torrent import org.transdroid.connect.model.*
import org.transdroid.connect.model.TorrentDetails
import org.transdroid.connect.model.TorrentStatus
import org.transdroid.connect.util.OkHttpBuilder import org.transdroid.connect.util.OkHttpBuilder
import org.transdroid.connect.util.flatten import org.transdroid.connect.util.flatten
import retrofit2.Retrofit import retrofit2.Retrofit
@ -65,6 +64,16 @@ class Rtorrent(private val configuration: Configuration) :
.addArrayDeserializer(TrackerSpec::class.java) { arrayValues -> .addArrayDeserializer(TrackerSpec::class.java) { arrayValues ->
TrackerSpec(arrayValues.asString(0)) 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()) .create())
.build().create(Service::class.java) .build().create(Service::class.java)
@ -134,6 +143,30 @@ class Rtorrent(private val configuration: Configuration) :
} }
} }
override fun files(torrent: Torrent): Flowable<TorrentFile> {
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<FileSpec, Int, Pair<Int, FileSpec>> { 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<TorrentDetails> { override fun details(torrent: Torrent): Single<TorrentDetails> {
return service.trackers(configuration.endpoint, torrent.uniqueId, "", "t.url=") return service.trackers(configuration.endpoint, torrent.uniqueId, "", "t.url=")
.flatten() .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 = private fun torrentTimeAdded(timeAdded: String?, timeCreated: Long): Date =
if (timeAdded.isNullOrBlank()) Date(timeCreated * 1000L) else Date(timeAdded!!.trim().toLong() * 1000L) if (timeAdded.isNullOrBlank()) Date(timeCreated * 1000L) else Date(timeAdded!!.trim().toLong() * 1000L)

4
connect/src/main/java/org/transdroid/connect/clients/rtorrent/Service.kt

@ -17,6 +17,10 @@ internal interface Service {
@POST("{endpoint}") @POST("{endpoint}")
fun torrents(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single<Array<TorrentSpec>> fun torrents(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single<Array<TorrentSpec>>
@XmlRpc("f.multicall")
@POST("{endpoint}")
fun files(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single<Array<FileSpec>>
@XmlRpc("t.multicall") @XmlRpc("t.multicall")
@POST("{endpoint}") @POST("{endpoint}")
fun trackers(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single<Array<TrackerSpec>> fun trackers(@Path("endpoint") endpoint: String?, @Body vararg args: String): Single<Array<TrackerSpec>>

8
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
}

23
connect/src/main/java/org/transdroid/connect/model/Torrent.kt

@ -87,22 +87,13 @@ data class Torrent(
fun mimicStop(): Torrent = mimicStatus(TorrentStatus.QUEUED) fun mimicStop(): Torrent = mimicStatus(TorrentStatus.QUEUED)
fun mimicNewLabel(newLabel: String): Torrent = Torrent(id, hash, name, statusCode, locationDir, rateDownload, rateUpload, seedersConnected, fun mimicChecking(): Torrent = mimicStatus(TorrentStatus.CHECKING)
seedersKnown, leechersConnected, leechersKnown, eta, downloadedEver, uploadedEver, totalSize, partDone, available, newLabel, dateAdded,
dateDone, error) fun mimicNewLabel(newLabel: String): Torrent = this.copy(label = newLabel)
fun mimicChecking(): Torrent { fun mimicNewLocation(newLocation: String): Torrent = this.copy(locationDir = newLocation)
return mimicStatus(TorrentStatus.CHECKING)
} private fun mimicStatus(newStatus: TorrentStatus): Torrent = this.copy(statusCode = newStatus)
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)
override fun toString(): String = "($uniqueId) $name" override fun toString(): String = "($uniqueId) $name"

14
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)
}

20
connect/src/test/java/org/transdroid/connect/clients/rtorrent/RtorrentLiveTest.kt

@ -43,18 +43,26 @@ class RtorrentLiveTest {
} }
@Test @Test
fun details() { fun torrents() {
rtorrent.details(firstLiveTorrent()) rtorrent.torrents()
.toList()
.test() .test()
.assertValue { it.trackers.isNotEmpty() } .assertValue { it.size > 0 }
} }
@Test @Test
fun torrents() { fun files() {
rtorrent.torrents() rtorrent.files(firstLiveTorrent())
.toList() .toList()
.test() .test()
.assertValue { torrents -> torrents.size > 0 } .assertValue { it.size > 0 }
}
@Test
fun details() {
rtorrent.details(firstLiveTorrent())
.test()
.assertValue { it.trackers.isNotEmpty() }
} }
@Test @Test

18
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.Configuration
import org.transdroid.connect.clients.Client import org.transdroid.connect.clients.Client
import org.transdroid.connect.mock.MockTorrent import org.transdroid.connect.mock.MockTorrent
import org.transdroid.connect.model.Priority
class RtorrentMockTest { class RtorrentMockTest {
@ -37,6 +38,23 @@ class RtorrentMockTest {
server.takeRequest() server.takeRequest()
} }
@Test
fun files() {
server.enqueue(mock("<array><data><value><array><data><value><string>ubuntu-17.04-desktop-amd64.iso</string></value><value><i8>1609039872</i8></value><value><i8>0</i8></value><value><i8>3069</i8></value><value><i8>1</i8></value><value><string>/downloads/ubuntu-17.04-desktop-amd64.iso</string></value></data></array></value></data></array>"))
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 @Test
fun details() { fun details() {
server.enqueue(mock("<array><data><value><array><data><value><string>http://torrent.ubuntu.com:6969/announce</string></value></data></array></value><value><array><data><value><string>http://ipv6.torrent.ubuntu.com:6969/announce</string></value></data></array></value></data></array>")) server.enqueue(mock("<array><data><value><array><data><value><string>http://torrent.ubuntu.com:6969/announce</string></value></data></array></value><value><array><data><value><string>http://ipv6.torrent.ubuntu.com:6969/announce</string></value></data></array></value></data></array>"))

Loading…
Cancel
Save