Skip to content

fix: normalize model download paths + handle download interupts#1655

Open
aminya wants to merge 9 commits intoComfy-Org:mainfrom
aminya:resume-download
Open

fix: normalize model download paths + handle download interupts#1655
aminya wants to merge 9 commits intoComfy-Org:mainfrom
aminya:resume-download

Conversation

@aminya
Copy link
Copy Markdown

@aminya aminya commented Mar 14, 2026

The model paths had to be normalized for the downloads. This fixes the download issue. But I also added better handling of interrupts in case of external issues.

An Electron event can be emitted with state === 'interrupted' when the download is paused due to an issue or network condition (e.g., a connection drop or timeout), not by the user. In that case, the download can be resumed.

Fixes Comfy-Org/ComfyUI#12929

Now the downloads work:

image

In case of a network interruption, the status of the download is now shown correctly when paused

image

┆Issue is synchronized with this Notion page by Unito

Summary by CodeRabbit

  • New Features

    • Downloads now auto-resume after interruptions with a capped retry limit and a short retry delay.
    • Interrupted downloads display a paused state with messaging indicating whether an automatic resume will be attempted.
  • Bug Fixes

    • More accurate progress and status reporting during interruptions and errors.
    • Failed or stopped downloads are removed to avoid stale entries.
    • User-provided save paths are normalized so temp and final files land in the expected models directory.

Electron can emit an updated event with state === 'interrupted' when the download is paused by the network (e.g. connection drop, timeout), not by the user. In that case the download can be resumed.
@aminya aminya requested a review from a team as a code owner March 14, 2026 09:41
@dosubot dosubot bot added size:S This PR changes 10-29 lines, ignoring generated files. bug Something isn't working labels Mar 14, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds auto-resume for interrupted downloads with per-download interruptResumeCount, reports interrupted state as PAUSED with progress and contextual message, schedules up to MAX_AUTO_RESUME_ATTEMPTS resume attempts (500ms delay), and centralizes UI-provided savePath resolution into models via resolveSavePath.

Changes

Cohort / File(s) Summary
Download manager & types
src/models/DownloadManager.ts
Introduced auto-resume: MAX_AUTO_RESUME_ATTEMPTS, interruptResumeCount on Download, interruption handling that emits PAUSED with computed progress and conditional resume messaging, schedules 500ms auto-resume when eligible, increments resume count on resume, and removes entries on unrecoverable error.
Path resolution & temp/local paths
src/models/DownloadManager.ts (resolveSavePath, getTempPath, getLocalSavePath, isPathInModelsDirectory)
Added resolveSavePath to normalize UI-provided absolute paths into the models directory; getTempPath and getLocalSavePath now use it; containment checks updated to use absolute-relative comparisons.
State mapping / progress reporting
src/models/DownloadManager.ts (convertDownloadState, progress emit paths)
Mapped INTERRUPTED -> PAUSED in state conversion; progress flow extended to emit PAUSED and resume messaging during interruption handling. Start/download startup flow updated to initialize localSavePath, tempPath, and interruptResumeCount.

Sequence Diagram(s)

sequenceDiagram
  participant UI as UI
  participant DM as DownloadManager
  participant NET as Network
  participant FS as Filesystem

  UI->>DM: startDownload(request with savePath)
  DM->>DM: resolveSavePath -> create Download entry (localSavePath, tempPath, item=null, interruptResumeCount=0)
  DM->>NET: begin transfer
  NET-->>DM: interruption/error
  DM->>DM: compute progress, map INTERRUPTED->PAUSED, set message (auto-resume eligible?)
  DM->>UI: report PAUSED + progress + message
  alt eligible to auto-resume
    DM->>DM: schedule resume after 500ms
    DM->>DM: increment interruptResumeCount
    DM->>NET: resume transfer (verify item still interrupted & URL matches)
    NET-->>DM: transfer continues/completes
    DM->>FS: write temp/local files using resolved savePath
    DM->>UI: report progress/completion
  else not eligible or error
    DM->>DM: delete download entry from internal map
    DM->>UI: report final error/state
  end
Loading

Poem

🐰 I paused a byte and gave a hop,
I counted tries before the stop.
Half a heartbeat, then a nudge—
A tiny resume, I softly judge.
Up to the limit, I nibble and pop.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main changes: normalizing download paths and handling download interrupts, which directly address the PR objectives.
Linked Issues check ✅ Passed The code changes implement auto-resume for interrupted downloads, path normalization, and paused-status reporting, all addressing the root cause of downloads stalling at 0% reported in #12929.
Out of Scope Changes check ✅ Passed All changes focus on download path normalization, interrupt handling, and progress reporting—directly scoped to resolving the download stall issue without unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/models/DownloadManager.ts`:
- Around line 72-79: The reportProgress call in DownloadManager
(reportProgress({..., status: DownloadStatus.PAUSED, message: 'Interrupted,
resuming…' })) is inconsistent with getAllDownloads() which currently normalizes
an 'interrupted' persisted status to DownloadStatus.ERROR; update the
normalization in getAllDownloads() so that when it encounters the persisted
'interrupted' state it maps to DownloadStatus.PAUSED (or the agreed
Interrupt/Paused enum value) instead of ERROR, ensuring both reportProgress and
getAllDownloads() use the same DownloadStatus for interrupted transfers.
- Around line 81-85: The timeout callback closes over the stale local variable
`download`, allowing a canceled or restarted download to be erroneously resumed;
change the guard inside the setTimeout in DownloadManager so it fetches the live
entry from the manager's map (e.g., look up the current download record by the
same key/id used in `cancelDownload`) and compare that liveEntry.item to `item`
and check `item.getState() === 'interrupted'` before calling `item.resume()`;
update the callback to use the live map lookup instead of `download.item ===
item`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 34f689a6-c1f5-4ccd-bc0f-5fd6dab99b4a

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce2bc9 and e764411.

📒 Files selected for processing (1)
  • src/models/DownloadManager.ts

@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Mar 14, 2026
@aminya aminya changed the title fix: auto-resume interrupted downloads fix: auto-resume network interrupted downloads Mar 14, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/models/DownloadManager.ts (1)

138-147: ⚠️ Potential issue | 🟠 Major

Guard stale done callbacks before reporting failure.

After cancelDownload() removes the old entry, the same URL can be started again before the cancelled item's done callback fires. This branch would then emit DownloadStatus.ERROR for the stale item, and delete(url) would remove the new live download. Re-check the current map entry against item before reporting or deleting.

Suggested fix
         } else {
+          const liveEntry = this.downloads.get(url);
+          if (liveEntry?.item !== item) {
+            return;
+          }
           log.info(`Download failed: ${state}`);
           const progress = item.getReceivedBytes() / item.getTotalBytes();
           this.reportProgress({
             url,
             filename: download.filename,
             progress,
             status: DownloadStatus.ERROR,
             savePath: download.savePath,
           });
           this.downloads.delete(url);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/DownloadManager.ts` around lines 138 - 147, Race condition: a
stale item's done callback can report ERROR and delete a new download for the
same URL. Before calling this.reportProgress(...) or this.downloads.delete(url)
in the done/error branch, re-check that this.downloads.get(url) === item and
abort if it differs; only emit DownloadStatus.ERROR and remove the entry when
the current map value is the same item instance. Use strict equality on the
stored entry (this.downloads.get(url)) to guard both the reportProgress call and
the delete call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/models/DownloadManager.ts`:
- Around line 198-205: The downloads map is storing savePath as the resolved
file path (localSavePath) but resumeDownload() expects a directory/path segment
when it calls startDownload(); this causes resumed downloads to restart into the
wrong destination. Change the download record to keep the original directory
(e.g., saveDir or directoryPath) as savePath and keep the resolved file path
separately (e.g., fullPath or filename+tempPath) so resumeDownload() can pass
the directory into startDownload(); update the object created in the
downloads.set(...) (and any uses of download.savePath in resumeDownload(),
startDownload(), and canResume()) to use the directory field when restarting and
the full/resolved path when performing file-level operations.

---

Outside diff comments:
In `@src/models/DownloadManager.ts`:
- Around line 138-147: Race condition: a stale item's done callback can report
ERROR and delete a new download for the same URL. Before calling
this.reportProgress(...) or this.downloads.delete(url) in the done/error branch,
re-check that this.downloads.get(url) === item and abort if it differs; only
emit DownloadStatus.ERROR and remove the entry when the current map value is the
same item instance. Use strict equality on the stored entry
(this.downloads.get(url)) to guard both the reportProgress call and the delete
call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bec166c9-7ef2-4b6f-bed5-2518e99e267b

📥 Commits

Reviewing files that changed from the base of the PR and between e764411 and afe80a6.

📒 Files selected for processing (1)
  • src/models/DownloadManager.ts

@aminya aminya changed the title fix: auto-resume network interrupted downloads fix: normalize model download paths + handle download interupts Mar 14, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/models/DownloadManager.ts`:
- Around line 329-337: The path containment check using startsWith is unsafe and
can be bypassed; update resolveSavePath and isPathInModelsDirectory to compute
const rel = path.relative(this.modelsDirectory, resolved) and then treat the
path as inside only if rel === '' || (!rel.startsWith('..' + path.sep) && rel
!== '..' && !path.isAbsolute(rel)); use that boolean to decide behavior in
resolveSavePath (compute and return path.dirname(rel) or rel when inside) and in
isPathInModelsDirectory (return the boolean), which will secure getLocalSavePath
and getTempPath against sibling-prefix escapes for this.modelsDirectory.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c3270ed0-e116-43aa-a657-dc1aca63b1c8

📥 Commits

Reviewing files that changed from the base of the PR and between afe80a6 and 50722f6.

📒 Files selected for processing (1)
  • src/models/DownloadManager.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/models/DownloadManager.ts (1)

322-330: ⚠️ Potential issue | 🔴 Critical

startsWith check can be bypassed; use path.relative() for safe containment.

The startsWith comparison at line 325 (and line 340 in isPathInModelsDirectory) treats sibling directories as valid children. For example, /models_evil/foo.safetensors starts with /models but is not inside it.

Suggested fix using path.relative()
  private resolveSavePath(savePath: string, filename: string): string {
    const base = path.resolve(this.modelsDirectory);
    const resolved = path.resolve(savePath);
-   if (resolved.startsWith(base)) {
-     const rel = path.relative(base, resolved);
+   const rel = path.relative(base, resolved);
+   const isInside = rel !== '' && rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
+   if (isInside) {
      return rel.endsWith(filename) ? path.dirname(rel) : rel;
    }
    return savePath;
  }

  private isPathInModelsDirectory(filePath: string): boolean {
    const absoluteFilePath = path.resolve(filePath);
    const absoluteModelsDir = path.resolve(this.modelsDirectory);
-   return absoluteFilePath.startsWith(absoluteModelsDir);
+   const rel = path.relative(absoluteModelsDir, absoluteFilePath);
+   return rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
  }

Based on learnings: "Use path.join() and path.sep to ensure file path handling works consistently across Windows, macOS, and Linux platforms."

Also applies to: 337-341

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/DownloadManager.ts` around lines 322 - 330, The current
startsWith-based containment checks in resolveSavePath and
isPathInModelsDirectory are unsafe (e.g., "/models_evil" passes) — replace them
with path.relative-based containment: compute const rel = path.relative(base,
resolved) after resolving both base (this.modelsDirectory) and the target path,
then treat the path as inside base only if rel is not empty, does not start with
'..' and is not equal to '..' (use path.sep awareness for Windows), and
normalize/trim separators as needed; update resolveSavePath to use that
containment test before returning a relative path and update
isPathInModelsDirectory to use the same path.relative-based logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/models/DownloadManager.ts`:
- Line 98: The progress calculation uses item.getReceivedBytes() /
item.getTotalBytes() without guarding against zero total; update both
occurrences where progress is computed (the expression using
item.getReceivedBytes() and item.getTotalBytes()) to check totalBytes > 0 first
(e.g., const total = item.getTotalBytes(); const progress = total > 0 ?
item.getReceivedBytes() / total : 0) so Infinity/NaN are never produced and the
UI always receives a valid number.
- Around line 87-96: The auto-resume scheduling in DownloadManager increments
interruptResumeCount only inside the setTimeout callback which allows multiple
timeouts to be scheduled and exceed MAX_AUTO_RESUME_ATTEMPTS; modify the
scheduling logic in the block where you check item.canResume() and
autoResumesLeft so you atomically reserve an auto-resume slot before calling
setTimeout — e.g., locate the entry via this.downloads.get(url), immediately
increment entry.interruptResumeCount (or set a boolean like
entry.autoResumeScheduled) and re-check autoResumesLeft, then schedule the
timeout that will call item.resume() only if entry.item === item and
item.getState() === 'interrupted'; ensure you decrement/clear the scheduled flag
only inside the callback so duplicate timeouts cannot be created.

---

Duplicate comments:
In `@src/models/DownloadManager.ts`:
- Around line 322-330: The current startsWith-based containment checks in
resolveSavePath and isPathInModelsDirectory are unsafe (e.g., "/models_evil"
passes) — replace them with path.relative-based containment: compute const rel =
path.relative(base, resolved) after resolving both base (this.modelsDirectory)
and the target path, then treat the path as inside base only if rel is not
empty, does not start with '..' and is not equal to '..' (use path.sep awareness
for Windows), and normalize/trim separators as needed; update resolveSavePath to
use that containment test before returning a relative path and update
isPathInModelsDirectory to use the same path.relative-based logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: db230c03-ae3d-4e04-b1af-923c59d0e61f

📥 Commits

Reviewing files that changed from the base of the PR and between 50722f6 and 235869c.

📒 Files selected for processing (1)
  • src/models/DownloadManager.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
src/models/DownloadManager.ts (3)

139-139: ⚠️ Potential issue | 🟡 Minor

Division by zero not guarded in done handler error path.

Same issue as line 98—apply the guard here as well.

Suggested fix
         } else {
           log.info(`Download failed: ${state}`);
-          const progress = item.getReceivedBytes() / item.getTotalBytes();
+          const totalBytes = item.getTotalBytes();
+          const progress = totalBytes > 0 ? item.getReceivedBytes() / totalBytes : 0;
           this.reportProgress({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/DownloadManager.ts` at line 139, In the DownloadManager done
handler compute of progress (const progress = item.getReceivedBytes() /
item.getTotalBytes();) you must guard against totalBytes being zero to avoid
division-by-zero; update the done handler in the DownloadManager (same pattern
used at line 98) to check item.getTotalBytes() > 0 before dividing and fallback
to a safe value (e.g., 0 or 1 as appropriate) when total is zero, ensuring
progress is always a finite number.

98-98: ⚠️ Potential issue | 🟡 Minor

Division by zero not guarded in progressing state.

Line 75 correctly guards against zero totalBytes, but line 98 does not. If getTotalBytes() returns 0 (e.g., server didn't send Content-Length), this produces Infinity or NaN.

Suggested fix
         } else if (state === 'progressing') {
-          const progress = item.getReceivedBytes() / item.getTotalBytes();
+          const totalBytes = item.getTotalBytes();
+          const progress = totalBytes > 0 ? item.getReceivedBytes() / totalBytes : 0;
           if (item.isPaused()) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/DownloadManager.ts` at line 98, The progress calculation uses
item.getReceivedBytes() / item.getTotalBytes() without guarding against a zero
total, causing Infinity/NaN; update the logic in the DownloadManager
class/method where progress is computed (the line that assigns to progress) to
check item.getTotalBytes() > 0 first and fall back to 0 (or the same safe
behavior used earlier at line 75) when totalBytes is zero or undefined so
progress never becomes Infinity/NaN.

87-96: ⚠️ Potential issue | 🟡 Minor

Race condition: multiple auto-resume timeouts can be scheduled.

If multiple 'interrupted' events fire within 500ms, each schedules a setTimeout before interruptResumeCount is incremented (since increment happens inside the callback). This can exceed MAX_AUTO_RESUME_ATTEMPTS.

Increment the count immediately when scheduling:

Suggested fix
           if (item.canResume() && autoResumesLeft > 0) {
+            // Increment immediately to prevent multiple scheduled resumes
+            if (liveEntry) {
+              liveEntry.interruptResumeCount = (liveEntry.interruptResumeCount ?? 0) + 1;
+            }
             setTimeout(() => {
               const entry = this.downloads.get(url);
               if (entry?.item === item && item.getState() === 'interrupted') {
-                entry.interruptResumeCount = (entry.interruptResumeCount ?? 0) + 1;
                 log.info('Auto-resuming interrupted download');
                 item.resume();
               }
             }, 500);
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/DownloadManager.ts` around lines 87 - 96, Race condition: multiple
timeouts can be scheduled because interruptResumeCount is incremented only
inside the setTimeout; increment the counter immediately when scheduling to
prevent exceeding MAX_AUTO_RESUME_ATTEMPTS. Locate the block using
item.canResume(), autoResumesLeft and this.downloads.get(url), and change the
logic so you read the map entry, compute and increment
entry.interruptResumeCount (entry.interruptResumeCount =
(entry.interruptResumeCount ?? 0) + 1) before calling setTimeout, and only
schedule the timeout if the new count is <= MAX_AUTO_RESUME_ATTEMPTS (or
autoResumesLeft > 0); keep the existing inside-timeout checks (entry?.item ===
item && item.getState() === 'interrupted') but do not increment there again.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/models/DownloadManager.ts`:
- Line 139: In the DownloadManager done handler compute of progress (const
progress = item.getReceivedBytes() / item.getTotalBytes();) you must guard
against totalBytes being zero to avoid division-by-zero; update the done handler
in the DownloadManager (same pattern used at line 98) to check
item.getTotalBytes() > 0 before dividing and fallback to a safe value (e.g., 0
or 1 as appropriate) when total is zero, ensuring progress is always a finite
number.
- Line 98: The progress calculation uses item.getReceivedBytes() /
item.getTotalBytes() without guarding against a zero total, causing
Infinity/NaN; update the logic in the DownloadManager class/method where
progress is computed (the line that assigns to progress) to check
item.getTotalBytes() > 0 first and fall back to 0 (or the same safe behavior
used earlier at line 75) when totalBytes is zero or undefined so progress never
becomes Infinity/NaN.
- Around line 87-96: Race condition: multiple timeouts can be scheduled because
interruptResumeCount is incremented only inside the setTimeout; increment the
counter immediately when scheduling to prevent exceeding
MAX_AUTO_RESUME_ATTEMPTS. Locate the block using item.canResume(),
autoResumesLeft and this.downloads.get(url), and change the logic so you read
the map entry, compute and increment entry.interruptResumeCount
(entry.interruptResumeCount = (entry.interruptResumeCount ?? 0) + 1) before
calling setTimeout, and only schedule the timeout if the new count is <=
MAX_AUTO_RESUME_ATTEMPTS (or autoResumesLeft > 0); keep the existing
inside-timeout checks (entry?.item === item && item.getState() ===
'interrupted') but do not increment there again.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 568bfb63-d4f2-4ce3-a3d9-499baa2c38c8

📥 Commits

Reviewing files that changed from the base of the PR and between 235869c and 1854980.

📒 Files selected for processing (1)
  • src/models/DownloadManager.ts

@beeteam368
Copy link
Copy Markdown

Hello, Is there a pre-release version that addresses this issue?

Thanks! Regards!

@aminya
Copy link
Copy Markdown
Author

aminya commented Mar 14, 2026

Hello, is there a pre-release version that addresses this issue?

Thanks! Regards!

Not yet until this is merged. Meanwhile, the only solution would be to build the code yourself, which is quite a task.

@benceruleanlu
Copy link
Copy Markdown
Member

Hi @aminya, thank you so much for this contribution!

I wanted to apologize—I actually missed this PR while working on a fix for the same bug recently. Since I didn't see this was already in progress, I ended up implementing a separate fix that was merged a few days ago.

That said, I see this PR includes great improvements for download recovery/resuming that weren't part of my change. We’d still love to get those features in! If you're interested in moving forward with the resuming logic, would you mind resolving the merge conflicts so we can take another look at those specific additions?

@aminya
Copy link
Copy Markdown
Author

aminya commented Mar 27, 2026

Hi @aminya, thank you so much for this contribution!

I wanted to apologize—I actually missed this PR while working on a fix for the same bug recently. Since I didn't see this was already in progress, I ended up implementing a separate fix that was merged a few days ago.

That said, I see this PR includes great improvements for download recovery/resuming that weren't part of my change. We’d still love to get those features in! If you're interested in moving forward with the resuming logic, would you mind resolving the merge conflicts so we can take another look at those specific additions?

@benceruleanlu Hi, you are welcome! That's totally fine. I have resolved the conflicts via a merge to preserve the review history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Your New Download Screen Doesn't Work

3 participants