diff --git a/README.md b/README.md index f76ff2f..fe57890 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,12 @@ For example, the following code snippet sequentially downloads the largest file val torrentUrl = Uri.parse("http://www.frostclick.com/torrents/video/animation/Big_Buck_Bunny_1080p_surround_frostclick.com_frostwire.com.torrent") val timeoutSeconds = 60 -val torrentSessionOptions = TorrentSessionOptions - .Builder(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)) - .onlyDownloadLargestFile(true) - .anonymousMode(true) - .stream(true) - .build() +val torrentSessionOptions = TorrentSessionOptions( + downloadLocation = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + , onlyDownloadLargestFile = true + , enableLogging = true + , shouldStream = true +) val torrentSession = TorrentSession(torrentUrl, torrentSessionOptions) @@ -54,7 +54,7 @@ and add the following in the dependent module: ```gradle dependencies { - implementation 'com.github.masterwok:simple-torrent-android:0.1.0' + implementation 'com.github.masterwok:simple-torrent-android:0.2.0' } ``` unless you're a fan of large APKs, you'll probably want to add the following to the build.gradle of your app so an APK is generated per ABI: diff --git a/app/src/main/java/com/masterwok/demosimpletorrentandroid/activities/MainActivity.kt b/app/src/main/java/com/masterwok/demosimpletorrentandroid/activities/MainActivity.kt index 4af7320..acd5a3d 100644 --- a/app/src/main/java/com/masterwok/demosimpletorrentandroid/activities/MainActivity.kt +++ b/app/src/main/java/com/masterwok/demosimpletorrentandroid/activities/MainActivity.kt @@ -43,12 +43,12 @@ class MainActivity : AppCompatActivity() { , "magnet:?xt=urn:btih:08ada5a7a6183aae1e09d831df6748d566095a10&dn=Sintel&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel.torrent" ) - private val torrentSessionOptions = TorrentSessionOptions - .Builder(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)) - .onlyDownloadLargestFile(true) - .logging(true) - .stream(true) - .build() + private val torrentSessionOptions = TorrentSessionOptions( + downloadLocation = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + , onlyDownloadLargestFile = true + , enableLogging = true + , shouldStream = true + ) private val torrentSessionPagerAdapter = TabFragmentPagerAdapter( supportFragmentManager diff --git a/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSession.kt b/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSession.kt index d5e1758..630da46 100644 --- a/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSession.kt +++ b/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSession.kt @@ -4,10 +4,7 @@ import android.content.Context import android.net.Uri import android.util.Log import android.webkit.URLUtil -import com.frostwire.jlibtorrent.AlertListener -import com.frostwire.jlibtorrent.Priority -import com.frostwire.jlibtorrent.SessionManager -import com.frostwire.jlibtorrent.TorrentHandle +import com.frostwire.jlibtorrent.* import com.frostwire.jlibtorrent.alerts.* import com.masterwok.simpletorrentandroid.contracts.TorrentSessionListener import com.masterwok.simpletorrentandroid.extensions.* @@ -43,6 +40,7 @@ class TorrentSession( private lateinit var saveLocationUri: Uri private lateinit var largestFileUri: Uri + private val sessionParams = SessionParams(torrentSessionOptions.settingsPack) private val alertListener = TorrentSessionAlertListener(this) private val dhtLock = Object() @@ -289,11 +287,11 @@ class TorrentSession( largestFileUri = Uri.EMPTY torrentSessionBuffer = TorrentSessionBuffer( - bufferSize = if (torrentSessionOptions.shouldStream) torrentSessionOptions.bufferSize else 0 + bufferSize = if (torrentSessionOptions.shouldStream) torrentSessionOptions.streamBufferSize else 0 ) if (!isRunning) { - start(torrentSessionOptions.build()) + start(sessionParams) } val path = torrentUri.toString() diff --git a/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSessionOptions.kt b/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSessionOptions.kt index ce8c721..bc6968a 100644 --- a/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSessionOptions.kt +++ b/simpletorrentandroid/src/main/java/com/masterwok/simpletorrentandroid/TorrentSessionOptions.kt @@ -1,7 +1,7 @@ package com.masterwok.simpletorrentandroid -import com.frostwire.jlibtorrent.SessionParams import com.frostwire.jlibtorrent.SettingsPack +import com.frostwire.jlibtorrent.swig.settings_pack import java.io.File @@ -10,118 +10,61 @@ import java.io.File * * For more information, [@see https://www.libtorrent.org/reference-Settings.html] */ -@Suppress("MemberVisibilityCanBePrivate") -class TorrentSessionOptions private constructor( - val bufferSize: Int - , val onlyDownloadLargestFile: Boolean - , val shouldStream: Boolean - , val downloadLocation: File - , val downloadRateLimit: Int - , val uploadRateLimit: Int - , val connectionsLimit: Int - , val dhtNodeMinimum: Int - , val dhtNodeLimit: Int - , val anonymousMode: Boolean - , val enableLogging: Boolean -) { - internal fun build(): SessionParams { - val settingsPack = SettingsPack() - .downloadRateLimit(downloadRateLimit) - .uploadRateLimit(uploadRateLimit) - .connectionsLimit(connectionsLimit) - .activeDhtLimit(dhtNodeLimit) - .anonymousMode(anonymousMode) - - return SessionParams(settingsPack) - } +@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter") +data class TorrentSessionOptions constructor( - @Suppress("unused") - class Builder constructor( - private val downloadLocation: File - ) { - private var onlyDownloadLargestFile: Boolean = false - private var shouldStream: Boolean = false - private var bufferSize: Int = 8 - private var downloadRateLimit: Int = 0 - private var uploadRateLimit: Int = 0 - private var connectionsLimit: Int = 200 - private var dhtNodeMinimum: Int = 10 - private var dhtNodeLimit: Int = 88 - private var anonymousMode: Boolean = false - private var enableLogging: Boolean = false + /** + * The root directory to download the torrent into. + */ + val downloadLocation: File /** - * Build the [TorrentSessionOptions] instance. + * If [onlyDownloadLargestFile] is true, then only the largest file in + * the torrent is downloaded. Default value is, false. */ - fun build(): TorrentSessionOptions { - return TorrentSessionOptions( - bufferSize - , onlyDownloadLargestFile - , shouldStream - , downloadLocation - , downloadRateLimit - , uploadRateLimit - , connectionsLimit - , dhtNodeMinimum - , dhtNodeLimit - , anonymousMode - , enableLogging - ) - } + , val onlyDownloadLargestFile: Boolean = false /** - * Enable verbose logging of the torrent session. + * When [useAnonymousMode]is true, the client tries to hide its identity to a certain + * degree. The peer-ID will no longer include the client's fingerprint. The user-agent + * will be reset to an empty string. Trackers will only be used if they are using + * a proxy server. The listen sockets are closed, and incoming connections will + * only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and + * is run on the same machine as the tracker proxy). Since no incoming + * connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all + * turned off when this setting is enabled. */ - fun logging(isEnabled: Boolean): Builder { - this.enableLogging = isEnabled - return this - } + , val anonymousMode: Boolean = false /** - * If [onlyDownloadLargestFile] is true, then only the largest file in - * the torrent is downloaded. Default value is, false. + * Enable verbose logging of the torrent session. */ - fun onlyDownloadLargestFile(onlyDownloadLargestFile: Boolean): Builder { - this.onlyDownloadLargestFile = onlyDownloadLargestFile - return this - } + , val enableLogging: Boolean = false /** * If [shouldStream] is true, then all downloaded files are downloaded * sequentially. Default value is, false */ - fun stream(shouldStream: Boolean): Builder { - this.shouldStream = shouldStream - return this - } + , val shouldStream: Boolean = false /** - * When streaming, the value of [bufferSize] is used to determine the maximum + * When streaming, the value of [streamBufferSize] is used to determine the maximum * number of pieces to prioritize in the [@see TorrentSessionBuffer]. Default * value, 8. */ - fun streamBufferSize(bufferSize: Int): Builder { - this.bufferSize = bufferSize - return this - } + , val streamBufferSize: Int = 8 /** * The session-global limits of upload and download rate limits, in bytes per second. * By default peers on the local network are not rate limited. Default value, 0 (infinity). */ - fun downloadRateLimit(downloadRateLimit: Int): Builder { - this.downloadRateLimit = downloadRateLimit - return this - } + , val downloadRateLimit: Int = 0 /** * The session-global limits of upload and download rate limits, in bytes per second. * By default peers on the local network are not rate limited. Default value, 0 (infinity). */ - fun uploadRateLimit(uploadRateLimit: Int): Builder { - this.uploadRateLimit = uploadRateLimit - return this - } + , val uploadRateLimit: Int = 0 /** * The global limit on the number of connections opened. The number of connections @@ -129,44 +72,42 @@ class TorrentSessionOptions private constructor( * connections limit, and open too many torrents, the limit will not be met. Default * value, 200. */ - fun connectionsLimit(connectionsLimit: Int): Builder { - this.connectionsLimit = connectionsLimit - return this - } + , val connectionsLimit: Int = 200 /** * The minimum number of DHT nodes to wait for until magnet link downloads will start. * Default value, 10. */ - fun dhtNodeMinimum(dhtMin: Int): Builder { - this.dhtNodeMinimum = dhtMin - return this - } + , val dhtNodeMinimum: Int = 10 /** * The max number of torrents to announce to the DHT. By default this is set to 88, * which is no more than one DHT announce every 10 seconds. */ - fun dhtNodeLimit(dhtLimit: Int): Builder { - this.dhtNodeLimit = dhtLimit - return this - } + , val dhtNodeLimit: Int = 88 +) { + val settingsPack: SettingsPack = SettingsPack() + .downloadRateLimit(downloadRateLimit) + .uploadRateLimit(uploadRateLimit) + .connectionsLimit(connectionsLimit) + .activeDhtLimit(dhtNodeLimit) + .anonymousMode(anonymousMode) + + init { + settingsPack.setString(settings_pack.string_types.dht_bootstrap_nodes.swigValue(), getDhtBootstrapNodeString()) + } - /** - * When [useAnonymousMode]is true, the client tries to hide its identity to a certain - * degree. The peer-ID will no longer include the client's fingerprint. The user-agent - * will be reset to an empty string. Trackers will only be used if they are using - * a proxy server. The listen sockets are closed, and incoming connections will - * only be accepted through a SOCKS5 or I2P proxy (if a peer proxy is set up and - * is run on the same machine as the tracker proxy). Since no incoming - * connections are accepted, NAT-PMP, UPnP, DHT and local peer discovery are all - * turned off when this setting is enabled. - */ - fun anonymousMode(useAnonymousMode: Boolean): Builder { - this.anonymousMode = useAnonymousMode - return this - } + /** + * Default list of DHT nodes. + */ + private fun getDhtBootstrapNodeString(): String = + "router.bittorrent.com:6681" + + ",dht.transmissionbt.com:6881" + + ",dht.libtorrent.org:25401" + + ",dht.aelitis.com:6881" + + ",router.bitcomet.com:6881" + + ",router.bitcomet.com:6881" + + ",dht.transmissionbt.com:6881" + + ",router.silotis.us:6881" // IPv6 - } } -