Skip to content

Refactor download architecture to be protocol-agnostic#124

Open
linroid wants to merge 6 commits intomainfrom
claude/refactor-download-architecture-fV3x2
Open

Refactor download architecture to be protocol-agnostic#124
linroid wants to merge 6 commits intomainfrom
claude/refactor-download-architecture-fV3x2

Conversation

@linroid
Copy link
Owner

@linroid linroid commented Feb 26, 2026

Summary

This PR refactors the core download engine to remove HTTP-specific assumptions and abstractions, enabling support for fundamentally different protocols like BitTorrent that don't fit the byte-range segment model.

Key Changes

  • Removed HTTP-specific fields from TaskRecord: etag, lastModified, and acceptRanges are now stored in the protocol-agnostic sourceResumeState field instead of being baked into the persistent record.

  • Eliminated HttpDownloadSource references from DownloadExecution: Added buildResumeState() and updateResumeState() methods to the DownloadSource interface so each source manages its own resume state lifecycle. DownloadExecution no longer directly references HTTP-specific metadata keys.

  • Extracted shared segmented download logic: Created SegmentedDownloadHelper to consolidate the nearly identical download loop, progress aggregation, and connection-change watcher logic that was duplicated between HTTP and FTP sources. Both sources now delegate to this helper instead of reimplementing the same logic.

  • Renamed headers parameter to properties: Changed DownloadSource.resolve() signature from headers: Map<String, String> to properties: Map<String, String> to reflect that non-HTTP sources interpret this parameter differently (or ignore it entirely).

  • Updated FileNameResolver to use ResolvedSource: Changed the interface to accept ResolvedSource instead of HTTP-specific ServerInfo, decoupling filename resolution from HTTP concerns.

  • Enhanced TorrentDownloadSource: Added buildResumeState() and updateResumeState() implementations to persist torrent-specific state (info hash, resume data, selected files) and track the active session for incremental updates.

Implementation Details

  • The SegmentedDownloadHelper accepts a generic downloadSegment lambda, allowing HTTP sources to use SegmentDownloader with HTTP Range requests while FTP sources provide their own FTP-specific download logic.

  • Resume state is now always populated via source.buildResumeState() during fresh downloads, eliminating the need for fallback logic in DownloadExecution.

  • The refactoring maintains backward compatibility with existing Segment-based progress reporting while allowing future protocols (like BitTorrent) to map their native concepts (pieces, bitfields) to the segment abstraction.

  • All protocol-specific metadata is now stored opaquely in SourceResumeState.data (JSON), making the core engine truly protocol-agnostic.

https://claude.ai/code/session_016RRifFe1AwMyjU8NYHrEqC

- Remove HTTP-specific fields (etag, lastModified, acceptRanges) from TaskRecord;
  all source-specific state now lives in sourceResumeState
- Add buildResumeState(ResolvedSource, Long) to DownloadSource interface so
  each source manages its own resume state
- Remove all HttpDownloadSource references from DownloadExecution (protocol-agnostic)
- Rename headers→properties in DownloadSource.resolve() and KetchApi.resolve()
- Update FileNameResolver to accept ResolvedSource instead of ServerInfo
- Update SQLite schema to persist sourceType and sourceResumeState instead of
  HTTP-specific columns
- Implement buildResumeState() in HttpDownloadSource, FtpDownloadSource, and
  TorrentDownloadSource

https://claude.ai/code/session_016RRifFe1AwMyjU8NYHrEqC
- Extract shared download loop (downloadSegments/downloadBatch) into
  SegmentedDownloadHelper, eliminating ~200 lines of duplicated code
  between HttpDownloadSource and FtpDownloadSource
- Both sources now delegate to SegmentedDownloadHelper.downloadAll()
  with a protocol-specific segment download lambda
- Fix headers→properties rename across all KetchApi implementations:
  RemoteKetch, InstanceManager, FakeKetchApi, ResolveUrlRequest,
  ServerRoutes
- Update all test fake DownloadSource objects: rename headers→properties,
  add buildResumeState() implementation
- Update DefaultFileNameResolverTest to use ResolvedSource instead of
  ServerInfo
- Remove obsolete TaskRecordTest for removed HTTP-specific fields
- Update DownloadContext KDoc to be protocol-agnostic

https://claude.ai/code/session_016RRifFe1AwMyjU8NYHrEqC
Add updateResumeState() to DownloadSource interface so sources can
persist resume state incrementally during download. This completes
the source-managed resume state lifecycle (Step 7 of the refactoring
plan).

- DownloadSource: new suspend fun updateResumeState() with default null
- DownloadExecution: save job now calls updateResumeState() and persists
  the result alongside segment snapshots
- TorrentDownloadSource: implements updateResumeState() to persist
  bitfield/resume data via activeSession, replacing the dead code in
  monitorProgress() that built resume state but never persisted it

https://claude.ai/code/session_016RRifFe1AwMyjU8NYHrEqC
Documents the 7-step plan for decoupling the download architecture
from HTTP-specific assumptions, enabling clean support for BitTorrent,
HLS, and future protocol sources.

https://claude.ai/code/session_016RRifFe1AwMyjU8NYHrEqC
@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.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Test Results

340 tests   - 705   340 ✅  - 705   1s ⏱️ -13s
 27 suites  -  67     0 💤 ±  0 
 27 files    -  67     0 ❌ ±  0 

Results for commit 9a4bd6c. ± Comparison against base commit ea01275.

This pull request removes 705 tests.
com.linroid.ketch.DownloadConfigTest ‑ invalidMaxSegments_throws[jvm]
com.linroid.ketch.DownloadConfigTest ‑ negativeMaxConcurrentDownloads_throws[jvm]
com.linroid.ketch.DownloadConfigTest ‑ negativeMaxConnectionsPerHost_throws[jvm]
com.linroid.ketch.DownloadConfigTest ‑ negativeRetryCount_throws[jvm]
com.linroid.ketch.DownloadConfigTest ‑ negativeRetryDelay_throws[jvm]
com.linroid.ketch.DownloadConfigTest ‑ zeroBufferSize_throws[jvm]
com.linroid.ketch.DownloadConfigTest ‑ zeroProgressInterval_throws[jvm]
com.linroid.ketch.app.FormatUtilsTest ‑ extractFilename_emptyUrl[jvm]
com.linroid.ketch.app.FormatUtilsTest ‑ extractFilename_simpleUrl[jvm]
com.linroid.ketch.app.FormatUtilsTest ‑ extractFilename_urlWithDeepPath[jvm]
…

♻️ This comment has been updated with latest results.

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.

2 participants