Skip to content

feat: add BitTorrent protocol support as pluggable DownloadSource#121

Merged
linroid merged 4 commits intomainfrom
feat/torrent-source
Feb 25, 2026
Merged

feat: add BitTorrent protocol support as pluggable DownloadSource#121
linroid merged 4 commits intomainfrom
feat/torrent-source

Conversation

@linroid
Copy link
Owner

@linroid linroid commented Feb 25, 2026

Summary

  • Add new library:torrent module implementing BitTorrent downloads via libtorrent4j (MIT license)
  • Support magnet links and .torrent files on Android and JVM, with iOS stubs (future cinterop)
  • Add managesOwnFileIo extension point to DownloadSource for sources that manage their own file I/O
  • Integrate torrent support into example apps with multi-file selector UI

Changes

Core infrastructure (library:core)

  • DownloadSource.managesOwnFileIo property (default false, backward-compatible)
  • NoOpFileAccessor internal stub for self-managed I/O sources
  • DownloadExecution conditionally skips FileAccessor creation/flush/cleanup

New module (library:torrent)

  • TorrentDownloadSource implementing DownloadSource — handles magnet: URIs and .torrent URLs
  • TorrentEngine/TorrentSession interfaces in commonMain (KMP-portable types only, designed for future iOS cinterop)
  • Libtorrent4jEngine/Libtorrent4jSession in jvmAndAndroid intermediate source set
  • Pure Kotlin utilities in commonMain: bencode encoder/decoder, magnet URI parser, info hash value class, .torrent metadata parser
  • iOS stubs via expect/actual factory pattern (throws KetchError.Unsupported)
  • Targets: Android, JVM, iosArm64, iosSimulatorArm64 (no WasmJs)
  • 176 tests across 14 test files

Example app (app/shared)

  • TorrentDownloadSource registered in platformAdditionalSources() for Android, JVM, iOS
  • File selector UI for multi-file torrents in AddDownloadDialog (checkboxes, select all/none)
  • Torrent metadata display (file count, source type)
  • Updated URL placeholder to mention magnet links

Architecture decisions

Decision Choice Rationale
Torrent engine libtorrent4j (MIT) Mature, MIT license, Maven Central, JVM+Android
Platforms Android + JVM now, iOS stub libtorrent4j is JVM-only; iOS via future cinterop
Source sets jvmAndAndroid intermediate Share libtorrent4j code between JVM and Android
Engine abstraction Interface (not expect/actual) Simpler; no iOS implementation yet
File I/O managesOwnFileIo flag Generic for any future self-managed source (HLS, etc.)
Segment mapping One segment per selected file Pieces managed by libtorrent; segments for progress UI

Known limitations

  • Resume data persistence not fully wired (libtorrent4j delivers resume data asynchronously via alerts)
  • Global speed limiter is approximate (torrent uses libtorrent's rate limiter independently from Ketch's TokenBucket)
  • iOS throws KetchError.Unsupported until native cinterop is implemented

Test plan

  • ./gradlew :library:torrent:jvmTest — 176 tests pass
  • ./gradlew :library:torrent:compileKotlinIosArm64 — iOS compilation succeeds
  • ./gradlew :library:torrent:compileKotlinIosSimulatorArm64 — iOS simulator compilation succeeds
  • ./gradlew :library:core:jvmTest — existing core tests unaffected
  • ./gradlew :app:shared:compileKotlinJvm — app integration compiles

Add a new `library:torrent` module implementing BitTorrent downloads via
libtorrent4j. Supports magnet links and .torrent files on Android and JVM,
with iOS stubs throwing KetchError.Unsupported (future cinterop work).

Core infrastructure:
- Add `managesOwnFileIo` property to DownloadSource interface for sources
  that manage their own file I/O (e.g., libtorrent4j)
- Add NoOpFileAccessor stub used when source handles file writes
- Update DownloadExecution to skip FileAccessor for self-managed sources

New library:torrent module:
- TorrentDownloadSource implementing DownloadSource (canHandle, resolve,
  download, resume) for magnet: URIs and .torrent URLs
- TorrentEngine/TorrentSession interfaces in commonMain with
  Libtorrent4jEngine/Session in jvmAndAndroid source set
- Pure Kotlin bencode encoder/decoder, magnet URI parser, info hash,
  and .torrent metadata parser in commonMain
- iOS stubs via expect/actual factory pattern (matching FTP module)
- 176 tests across 14 test files

Example app integration:
- Register TorrentDownloadSource in platform sources (Android, JVM, iOS)
- File selector UI for multi-file torrents in AddDownloadDialog
- Updated URL placeholder to mention magnet links
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Version 2.1.0-31 only included x86_64 macOS native binary. Version
2.1.0-39 includes arm64, fixing LinkageError on Apple Silicon Macs.
@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Test Results

1 121 tests  +176   1 121 ✅ +176   13s ⏱️ -1s
   98 suites + 16       0 💤 ±  0 
   98 files   + 16       0 ❌ ±  0 

Results for commit 750e01d. ± Comparison against base commit 543ba18.

♻️ This comment has been updated with latest results.

The libtorrent4j native library (.dylib/.so/.dll) is bundled inside
platform-specific JARs but System.loadLibrary() can't find libraries
inside JARs. NativeLibraryLoader extracts the native lib to a temp
directory and sets the libtorrent4j.jni.path system property so the
SWIG static initializer uses System.load() with the absolute path.
assert() requires @ExperimentalNativeApi opt-in on Kotlin/Native,
causing iOS test compilation to fail.
@linroid linroid merged commit d3b12a6 into main Feb 25, 2026
6 checks passed
@linroid linroid deleted the feat/torrent-source branch February 25, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant