Skip to content

feat(downloader): mejorar descargas (concurrencia, reintentos, omitir…#4551

Open
Rocket-Space wants to merge 1 commit into
pear-devs:masterfrom
Rocket-Space:feat/mejorar-descargas
Open

feat(downloader): mejorar descargas (concurrencia, reintentos, omitir…#4551
Rocket-Space wants to merge 1 commit into
pear-devs:masterfrom
Rocket-Space:feat/mejorar-descargas

Conversation

@Rocket-Space

@Rocket-Space Rocket-Space commented Jul 3, 2026

Copy link
Copy Markdown

… duplicados)

Summary by CodeRabbit

  • New Features

    • Added a new downloader setting to control how many downloads can run at once.
    • The downloader menu now includes options to choose the parallel download limit.
  • Bug Fixes

    • Improved download reliability with automatic retries before showing an error.
    • Download progress and counters now update more consistently during playlist downloads.
    • Existing-file handling now completes progress updates correctly and skips the item cleanly.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a configurable maxParallelDownloads setting to the downloader plugin, with retry logic (up to 3 attempts) for song downloads, a concurrency-limited parallel playlist download pipeline with progress tracking and failure retry prompting, plus corresponding menu UI and i18n additions.

Changes

Parallel Downloads Feature

Layer / File(s) Summary
Config schema and defaults
src/plugins/downloader/index.ts
Adds optional maxParallelDownloads?: number to DownloaderPluginConfig and sets maxParallelDownloads: 1 in defaultConfig.
Retry logic for song downloads
src/plugins/downloader/main/index.ts
downloadSong and downloadSongFromId gain a skipErrorDialog parameter and retry up to 3 times with a 1s delay on failure; existing-file early return now unconditionally closes feedback and advances progress, removing prior skipExisting gating.
Concurrency-limited playlist download pipeline
src/plugins/downloader/main/index.ts
Reworks playlist downloading into a parallel pipeline using a new PlaylistItem interface and runWithConcurrencyLimit scheduler, aggregating progress/badge updates and prompting users to retry failed songs recursively.
Menu UI and translations
src/plugins/downloader/menu.ts, src/i18n/resources/en.json, src/i18n/resources/es.json
Adds a "max parallel downloads" submenu with radio options 1–5 tied to config, and corresponding English/Spanish translation labels.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Menu
  participant downloadPlaylistItems
  participant runWithConcurrencyLimit
  participant downloadSongFromId

  User->>Menu: Set maxParallelDownloads
  Menu->>downloadPlaylistItems: Start playlist download
  downloadPlaylistItems->>runWithConcurrencyLimit: Schedule items with concurrency limit
  runWithConcurrencyLimit->>downloadSongFromId: Download song (retry up to 3x)
  downloadSongFromId-->>runWithConcurrencyLimit: Success or failure
  runWithConcurrencyLimit-->>downloadPlaylistItems: Aggregate progress and failures
  downloadPlaylistItems->>User: Prompt retry for failed songs
  User-->>downloadPlaylistItems: Confirm retry
  downloadPlaylistItems->>downloadPlaylistItems: Recursively rerun failed subset
Loading

Estimated code review effort: 3 (Moderate) | ~25 minutes

Related issues: None found in the provided context.

Related PRs: None found in the provided context.

Suggested labels: enhancement, downloader-plugin, i18n

Suggested reviewers: None found in the provided context.

🐰 A rabbit taps its paw with glee,
Five downloads run where one used to be,
Retry thrice if songs go astray,
A menu radio to have its say,
Simultaneous tunes, hop hop hooray!

🚥 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 El título describe bien las mejoras principales del descargador: concurrencia, reintentos y omisión de duplicados.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c852e7f226

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const filePath = join(dir, filename);

if (config.skipExisting && existsSync(filePath)) {
if (existsSync(filePath)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor skip-existing before skipping files

When a user has Skip existing files disabled and tries to re-download a song whose target filename already exists (for example to overwrite a corrupt file or use a different preset/tags), this now returns solely because the file exists, so no download or write occurs. The previous behavior gated this early return on config.skipExisting; please restore that check so the menu toggle still controls whether existing files are skipped.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/plugins/downloader/main/index.ts`:
- Around line 841-845: The worker wrapper in the scheduler is swallowing all
errors from the concurrent task, which prevents unexpected failures from
reaching the outer downloadPlaylist error handling. Update the concurrent task
handling around fn(item, currentIndex) so it does not catch every exception
here; let non-song-specific failures propagate naturally, while keeping the
existing per-song failure handling elsewhere intact.
- Around line 471-475: The existing-file early return in the downloader logic
ignores config.skipExisting, so files are being treated as completed duplicates
even when skipping is disabled. Update the existing-file branch in the same area
as the file download flow to only return early when config.skipExisting is true,
and otherwise continue with the normal download path; keep the behavior aligned
with the playlist-folder check and the surrounding file-handling logic in
index.ts.
- Around line 923-932: The retry-failed dialog in the downloader flow is
hard-coded in Spanish instead of using the existing localization system. Update
the dialog in the failedSongs block within the downloader main index logic to
use t(...) for the title, buttons, message, and detail text so it matches the
surrounding user-facing dialogs. Keep the failedSongs mapping and
dialog.showMessageBoxSync flow, but replace the literal strings with
localization keys and interpolated values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a84acbc-dc8f-410d-9940-7903e646287a

📥 Commits

Reviewing files that changed from the base of the PR and between 16da6d3 and c852e7f.

📒 Files selected for processing (5)
  • src/i18n/resources/en.json
  • src/i18n/resources/es.json
  • src/plugins/downloader/index.ts
  • src/plugins/downloader/main/index.ts
  • src/plugins/downloader/menu.ts

Comment on lines +471 to 475
if (existsSync(filePath)) {
sendFeedback(null, -1);
increasePlaylistProgress(1.0);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Respect config.skipExisting before skipping existing files.

Line 471 now treats every existing file as a completed duplicate, even when config.skipExisting is false. That makes the file-level behavior inconsistent with the playlist-folder check at Lines 761-771 and can silently skip requested downloads.

Proposed fix
-  if (existsSync(filePath)) {
+  if (config.skipExisting && existsSync(filePath)) {
     sendFeedback(null, -1);
     increasePlaylistProgress(1.0);
     return;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (existsSync(filePath)) {
sendFeedback(null, -1);
increasePlaylistProgress(1.0);
return;
}
if (config.skipExisting && existsSync(filePath)) {
sendFeedback(null, -1);
increasePlaylistProgress(1.0);
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/downloader/main/index.ts` around lines 471 - 475, The
existing-file early return in the downloader logic ignores config.skipExisting,
so files are being treated as completed duplicates even when skipping is
disabled. Update the existing-file branch in the same area as the file download
flow to only return early when config.skipExisting is true, and otherwise
continue with the normal download path; keep the behavior aligned with the
playlist-folder check and the surrounding file-handling logic in index.ts.

Comment on lines +841 to +845
try {
await fn(item, currentIndex);
} catch (e) {
console.error('Error in concurrent task:', e);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not swallow unexpected worker failures in the scheduler.

Per-song download failures are already captured in Lines 898-909. Catching every worker error here hides unexpected failures from the outer downloadPlaylist error path.

Proposed fix
-    try {
-      await fn(item, currentIndex);
-    } catch (e) {
-      console.error('Error in concurrent task:', e);
-    }
+    await fn(item, currentIndex);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
await fn(item, currentIndex);
} catch (e) {
console.error('Error in concurrent task:', e);
}
await fn(item, currentIndex);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/downloader/main/index.ts` around lines 841 - 845, The worker
wrapper in the scheduler is swallowing all errors from the concurrent task,
which prevents unexpected failures from reaching the outer downloadPlaylist
error handling. Update the concurrent task handling around fn(item,
currentIndex) so it does not catch every exception here; let non-song-specific
failures propagate naturally, while keeping the existing per-song failure
handling elsewhere intact.

Comment on lines +923 to +932
if (failedSongs.length > 0) {
const failedListText = failedSongs.map(s => `- ${s.authorName} - ${s.title}`).join('\n');
const response = dialog.showMessageBoxSync(win, {
type: 'warning',
buttons: ['Reintentar', 'Cancelar'],
defaultId: 0,
title: 'Descargas fallidas',
message: `Las siguientes canciones fallaron después de 3 intentos:`,
detail: `${failedListText}\n\n¿Desea reintentar la descarga de estas canciones?`,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the retry-failed dialog text.

This user-facing dialog is hard-coded in Spanish, unlike the surrounding downloader dialogs that use t(...).

Proposed direction
-      buttons: ['Reintentar', 'Cancelar'],
+      buttons: [
+        t('plugins.downloader.backend.dialog.failed-downloads.buttons.retry'),
+        t('plugins.downloader.backend.dialog.failed-downloads.buttons.cancel'),
+      ],
       defaultId: 0,
-      title: 'Descargas fallidas',
-      message: `Las siguientes canciones fallaron después de 3 intentos:`,
-      detail: `${failedListText}\n\n¿Desea reintentar la descarga de estas canciones?`,
+      title: t('plugins.downloader.backend.dialog.failed-downloads.title'),
+      message: t('plugins.downloader.backend.dialog.failed-downloads.message'),
+      detail: t('plugins.downloader.backend.dialog.failed-downloads.detail', {
+        failedListText,
+      }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (failedSongs.length > 0) {
const failedListText = failedSongs.map(s => `- ${s.authorName} - ${s.title}`).join('\n');
const response = dialog.showMessageBoxSync(win, {
type: 'warning',
buttons: ['Reintentar', 'Cancelar'],
defaultId: 0,
title: 'Descargas fallidas',
message: `Las siguientes canciones fallaron después de 3 intentos:`,
detail: `${failedListText}\n\n¿Desea reintentar la descarga de estas canciones?`,
});
if (failedSongs.length > 0) {
const failedListText = failedSongs.map(s => `- ${s.authorName} - ${s.title}`).join('\n');
const response = dialog.showMessageBoxSync(win, {
type: 'warning',
buttons: [
t('plugins.downloader.backend.dialog.failed-downloads.buttons.retry'),
t('plugins.downloader.backend.dialog.failed-downloads.buttons.cancel'),
],
defaultId: 0,
title: t('plugins.downloader.backend.dialog.failed-downloads.title'),
message: t('plugins.downloader.backend.dialog.failed-downloads.message'),
detail: t('plugins.downloader.backend.dialog.failed-downloads.detail', {
failedListText,
}),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/downloader/main/index.ts` around lines 923 - 932, The
retry-failed dialog in the downloader flow is hard-coded in Spanish instead of
using the existing localization system. Update the dialog in the failedSongs
block within the downloader main index logic to use t(...) for the title,
buttons, message, and detail text so it matches the surrounding user-facing
dialogs. Keep the failedSongs mapping and dialog.showMessageBoxSync flow, but
replace the literal strings with localization keys and interpolated values.

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