An Android download manager for direct HTTP/HTTPS files, BitTorrent/Magnet transfers, public audio streams, and public video/HLS streams, focused on transfer integrity, path containment, and truthful source reporting.
- RFC 9110 Range Validation: Multi-segment parallel transfers require
HTTP 206 Partial Contentwith a matchingContent-Rangeheader (start-end/total) and exact byte count per segment. - Automatic Single-Stream Fallback: If a server ignores
Rangeand returnsHTTP 200 OK, parallel segments are aborted and the download falls back cleanly to a single stream. - Validator-Backed Resume: Persists
ETagandLast-Modifiedvalidators in Room (v2), usesIf-Rangeon resume, and handles416 Range Not Satisfiablesafely. - Per-Job Staging & Collision-Safe Finalization: Writes to
.filename.part.<id>and promotes to the final filename only after byte-length and optional SHA-256 verification.
- Strict Path Containment: All torrent metadata paths are validated via
TorrentPathSafetyagainst parent traversal (..), absolute paths, backslashes, NUL/control characters, symlinks, and duplicate normalized paths. - Piece SHA-1 & Length Verification: Multi-file and single-file transfers are staged per job and verified against declared file lengths and piece SHA-1 hashes across file boundaries before promotion.
- Truthful Swarm Metrics: Displays measured tracker/peer counts (
0when measured zero,Unavailablewhen unmeasured) without synthetic floors. - Per-File Lifecycle States: Tracks
UNSELECTED,QUEUED,PARTIAL,VERIFIED,UNVERIFIED_COMPLETE, andFAILEDstates per file.
- Strict Host Classification: Uses
UrlClassifieron parsed URL hosts to prevent lookalike domain or query-string spoofing. - Public Audio Support: Direct audio files (
MP3,FLAC,M4A,OGG,WAV,OPUS), SoundCloud, YouTube/YouTube Music, Bandcamp, Audiomack, Mixcloud, Jamendo, and Internet Archive. - Honest Format & Provenance Reporting: Never labels an
M4AorMP3stream asFLAC. Spotify links download the public 30-second preview (explicitly labeled30s Preview) unless the user explicitly enables Allow Alternate-Source Lookup (YouTube), which verifies title, artist, and duration before matching. - Public Video & HLS Support: Supports YouTube, TikTok, Instagram, X/Twitter, Facebook, Reddit, Vimeo, Dailymotion, Archive.org, and non-encrypted VOD HLS (
.m3u8) playlists (including master variant selection,#EXT-X-MAP, and#EXT-X-BYTERANGE). - Unsupported Sources: Live HLS streams, encrypted HLS (
#EXT-X-KEYwithMETHOD != NONE), Twitch, and DRM-protected subscription services (Apple Music, Deezer, Tidal, Netflix, Disney+, Prime Video) are explicitly reported as unsupported.
- Queue & Concurrency Control: Enforces
max_concurrent_downloadsanduse_mobile_data(Wi-Fi-only policy pauses/defers active transfers when switching to metered networks). - Serialized Worker Cancellation: Awaits coroutine termination (
stopAndAwait) before state transitions and guards progress updates withWHERE id = :id AND status = 'DOWNLOADING'. - Android 15 (
targetSdk 36) Lifecycle: UsesWorkManager(DownloadRecoveryWorker) onBOOT_COMPLETEDinstead of launching adataSyncforeground service from boot, and handlesService.onTimeout.
- Direct HTTP/HTTPS, HLS, and BitTorrent transfers connect directly from the device to the target hosts, trackers, and peers.
- When Allow Third-Party Social Video Resolvers is enabled in Settings (default: on), social video URLs (TikTok, X/Twitter, Instagram) may be queried against public resolver endpoints (
tikwm.com,vxtwitter.com,fxtwitter.com,ddinstagram.com) when direct page extraction is restricted. This can be disabled in Settings. - When Allow Alternate-Source Lookup (YouTube) is enabled by the user (default: off), track metadata (title and artist) may be searched on YouTube to find a verified full-length match.
- JDK: Java 21
- Android SDK:
compileSdk = 36,targetSdk = 36,minSdk = 24 - Gradle Wrapper: Gradle
9.3.1with pinned SHA-256 distribution checksum ingradle/wrapper/gradle-wrapper.properties
# Build Debug APK
./gradlew assembleDebug
# Run Unit & Regression Test Suite
./gradlew :app:testDebugUnitTest
# Run Android Lint
./gradlew :app:lintDebug