Skip to content

feat: track download progress in SQLite #12

@roziscoding

Description

@roziscoding

Summary

Persist Jack download progress in SQLite so a future Jack UI/API can show active, completed, and failed downloads without scraping logs or scanning folders.

Motivation

The blackhole flow cannot report native progress back to Radarr/Sonarr, and Jack currently only emits progress as logs while the HTTP stream is active. A Jack UI needs a first-class download state source.

Proposed Scope

  • Add a SQLite-backed download registry using bun:sqlite.
  • Do not create a progress row immediately after parsing the .torrent stub. Stub parsing only gives torrentFilename, peerId, and itemId, which is not enough for a useful download record.
  • Create the download progress row after BlackholeWatcher.processTorrent() successfully resolves peer.getRelease(itemId), when Jack knows the media filename, destination path, part path, and release metadata.
  • Store one row per actual peer file download with fields like:
    • torrentFilename
    • peerId / peerName
    • itemId
    • filename
    • destPath / partPath
    • releaseSize from peer release metadata, for early display before the file response starts
    • expectedBytes, nullable until the peer file response headers are received
    • downloadedBytes
    • status (downloading, completed, failed, import_queued)
    • startedAt, updatedAt, completedAt
    • error
  • Metadata lookup failures before a progress row exists should still be logged with torrentFilename, peerId, and itemId; they do not need to appear as download progress records unless a separate event/audit table is added later.
  • Set releaseSize when the row is created from peer.getRelease(itemId).
  • Set authoritative expectedBytes when PeerConnector.downloadFile() receives the peer file response:
    • For full downloads, use the Content-Length header from GET /peer/items/:id/file.
    • If Content-Length is missing or invalid, keep expectedBytes null and expose indeterminate progress, or fail explicitly if the download path requires known length.
    • If Content-Length differs from releaseSize, store/log the mismatch; the response header is authoritative for the active transfer.
    • feat: harden peer download handling #13 will extend this for resumed downloads by using the total from Content-Range on 206 Partial Content responses.
  • Update PeerConnector.downloadFile() to accept a progress callback or state updater instead of only logging progress.
  • Add API endpoints for a future UI, for example GET /downloads and GET /downloads/:id.
  • Add startup reconciliation for stale downloading rows, using .part file size when available and marking unrecoverable rows as failed/stale.

Acceptance Criteria

  • Active download progress is queryable from Jack without reading logs.
  • Progress rows are created only after release metadata is available.
  • Progress survives Jack restart via SQLite persistence.
  • Completed and failed downloads keep useful final state and errors.
  • Download state updates include torrent filename metadata.
  • expectedBytes source is clear and covered by tests for normal, missing, and mismatched response length cases.
  • Tests cover registry persistence, progress updates, completion, failure, and restart reconciliation.

Out of Scope

  • Reporting progress inside Radarr/Sonarr. That would require Jack to implement a real supported download-client API shim instead of the current Torrent Blackhole flow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions