[PR #3] [CLOSED] Add download progress tracking and mobile layout improvements to AudioPlayer #112

Closed
opened 2026-03-03 00:07:50 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/binimum/tidal-ui/pull/3
Author: @Copilot
Created: 10/5/2025
Status: Closed

Base: mainHead: copilot/fix-4f9af99b-c92d-441e-8ce8-89a5f0af9bf3


📝 Commits (2)

  • 36a1eeb Initial plan
  • 0c97c8a Add downloads store and FFmpeg/track download banners

📊 Changes

5 files changed (+557 additions, -76 deletions)

View changed files

📝 src/lib/api.ts (+50 -5)
📝 src/lib/components/AudioPlayer.svelte (+155 -53)
📝 src/lib/components/TrackList.svelte (+36 -9)
📝 src/lib/ffmpegClient.ts (+70 -9)
src/lib/stores/downloads.ts (+246 -0)

📄 Description

Overview

This PR enhances the AudioPlayer component with comprehensive download progress tracking, improved mobile layout, and better user control over downloads. All changes maintain backward compatibility and follow Svelte 5 best practices.

Features Added

1. FFmpeg Download Banner with Countdown

When FFmpeg WASM needs to be downloaded for metadata embedding, a banner now appears above the AudioPlayer with a 5-second countdown:

  • Shows "Downloading FFmpeg (32 MB) in X seconds..." with a cancellable countdown
  • Transitions to a progress bar during the actual download
  • Automatically skips the countdown when a user initiates a track download
  • Auto-dismisses after completion

This gives users visibility into system-level operations and control over when FFmpeg loads.

2. Real-Time Track Download Progress

Each downloading track now displays a dedicated progress banner above the AudioPlayer:

  • Shows album artwork, track title, and artist name
  • Real-time progress bar with percentage (0-99% for download, 99-100% for metadata processing)
  • Status text updates: "Downloading... X%" → "Processing metadata... X%"
  • Cancel button (X) to abort the download at any time
  • Multiple simultaneous downloads stack vertically

Users can now monitor download progress and manage multiple downloads simultaneously.

3. Queue Panel Slide Animation

The queue panel now smoothly slides in and out with a 300ms transition when opened or closed, providing a more polished user experience.

4. Mobile-Optimized Two-Line Layout

The AudioPlayer now uses a responsive two-line layout on mobile devices (<640px):

Mobile:

  • Line 1: Album cover + track info (title, artist, quality)
  • Line 2: Playback controls + queue button

Desktop (≥640px):

  • Single-line layout with all elements visible including volume control

This fixes the issue where buttons covered the track title on mobile devices.

5. Download Cancellation

The download button in TrackList now shows an X icon while downloading (instead of a spinner). Clicking it immediately cancels the download:

  • Uses AbortController for clean fetch request cancellation
  • Progress banner disappears from the AudioPlayer
  • Button returns to the download icon state

Technical Implementation

New Downloads Store

Created src/lib/stores/downloads.ts to centralize all download state management:

  • FFmpeg loading state (idle, countdown, loading, loaded, error)
  • Track download states with progress tracking
  • Derived stores for reactive UI updates
  • Methods for progress updates, cancellation, and cleanup

Progress Tracking

Enhanced losslessAPI.downloadTrack() to support progress callbacks using ReadableStream:

const reader = response.body.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  received += value.length;
  onProgress(Math.round((received / total) * 100));
}

Clean Cancellation

Downloads can be cancelled cleanly using AbortController:

  • Fetch requests are aborted mid-stream
  • Partial data is discarded
  • UI state updates to reflect cancellation
  • No memory leaks from pending operations

Responsive Design

Uses Tailwind CSS responsive classes for mobile/desktop layouts:

  • flex-col on mobile for stacked layout
  • flex-row on desktop for horizontal layout
  • hidden sm:flex pattern for mobile-hidden elements
  • Standard sm: breakpoint (640px)

Files Changed

New:

  • src/lib/stores/downloads.ts (235 lines) - Downloads state management

Modified:

  • src/lib/ffmpegClient.ts (+75 lines) - Countdown and progress tracking
  • src/lib/api.ts (+45 lines) - Progress callbacks and AbortSignal support
  • src/lib/components/AudioPlayer.svelte (+120 lines) - Download banners, mobile layout, animations
  • src/lib/components/TrackList.svelte (+40 lines) - Cancellation support

Total: ~515 lines added/modified

Testing

  • TypeScript compilation: No errors
  • Svelte check: Passes (0 errors, 1 pre-existing warning)
  • Build: Succeeds
  • Uses Svelte 5 runes throughout
  • Follows existing code patterns

Manual testing recommended for:

  • Visual appearance of download banners
  • Progress bar accuracy
  • Download cancellation
  • Mobile/desktop responsive behavior
  • Multiple simultaneous downloads

Screenshots

The implementation adds visual feedback for all download operations while maintaining the existing AudioPlayer design aesthetic. Download banners use the same dark theme with blue accents for system operations (FFmpeg) and gray for user actions (track downloads).

Fixes #[issue-number]

Original prompt
  1. Can you add a little menu on top of the AudioPlayer that sticks up from it that says 'Downloading FFMpeg (xx MB) in 5 seconds...' with an X and then transitions into a progress bar? Also, if a user downloads a song, then skip the countdown.
  2. Can you make the track downloading more verbose, like a progress bar with the percentage, like a attachment on top of AudioPlayer.svelte, the same as the FFMpeg thing with an X to cancel it?
  3. Can you add a slide out/in animation to the queue opening?
  4. On mobile, the buttons cover up the title, so can you make it two lines on mobile only, with album cover, title at the top and controls at the bottom?
  5. Can you make it so that pressing the spinning circle on a downloading track stops the download and then shows an X to signify that?
    xt": "shoot "
    },
    {
    "time": 228489,
    "duration": 632,
    "text": "across "
    },
    {
    "time": 229121,
    "duration": 3519,
    "text": "the"
    }
    ],
    "element": {
    "key": "L55",
    "songPart": "Outro",
    "singer": "v2000"
    }
    },
    {
    "time": 233064,
    "duration": 5111,
    "text": "That we shoot across the sky",
    "syllabus": [
    {
    "time": 233064,
    "duration": 224,
    "text": "That "
    },
    {
    "time": 233288,
    "duration": 216,
    "text": "we "
    },
    {
    "time": 233504,
    "duration": 398,
    "text": "shoot "
    },
    {
    "time": 233902,
    "duration": 600,
    "text": "across "
    },
    {
    "time": 234502,
    "duration": 216,
    "text": "the "
    },
    {
    "time": 234718,
    "duration": 3457,
    "text": "sky"
    }
    ],
    "element": {
    "key": "L56",
    "songPart": "Outro",
    "singer": "v2000"
    }
    },
    {
    "time": 238296,
    "duration": 3638,
    "text": "That we shoot across the",
    "syllabus": [
    {
    "time": 238296,
    "duration": 298,
    "text": "That "
    },
    {
    "time": 238594,
    "duration": 152,
    "text": "we "
    },
    {
    "time": 238746,
    "duration": 349,
    "text": "shoot "
    },
    {
    "time": 239095,
    "duration": 651,
    "text": "across "
    },
    {
    "time": 239746,
    "duration": 2188,
    "text": "the"
    }
    ],
    "element": {
    "key": "L57",
    "songPart": "Outro",
    "singer": "v2000"
    }
    },
    {
    "time": 243701,
    "duration": 5323,
    "text": "That we shoot across the sky",
    "syllabus": [
    {
    "time": 243701,
    "duration": 270,
    "text": "That "
    },
    {
    "time": 243971,
    "duration": 200,
    "text": "we "
    },
    {
    "time": 244171,
    "duration": 368,
    "text": "shoot "
    },
    {
    "time": 244539,
    "duration": 667,
    "text": "across "
    },
    {
    "time": 245206,
    "duration": 381,
    "text": "the "
    },
    {
    "time": 245587,
    "duration": 3437,
    "text": "sky"
    }
    ],
    "element": {
    "key": "L58",
    "songPart": "Outro",
    "singer": "v2000"
    }
    },
    {
    "time": 249395,
    "duration": 3154,
    "text": "That we shoot across the",
    "syllabus": [
    {
    "time": 249395,
    "duration": 316,
    "text": "That "
    },
    {
    ...
Created from VS Code via the [GitHub Pull Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) extension.

Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/binimum/tidal-ui/pull/3 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 10/5/2025 **Status:** ❌ Closed **Base:** `main` ← **Head:** `copilot/fix-4f9af99b-c92d-441e-8ce8-89a5f0af9bf3` --- ### 📝 Commits (2) - [`36a1eeb`](https://github.com/binimum/tidal-ui/commit/36a1eebebaf4c01da22339acbd9b5a03d336db85) Initial plan - [`0c97c8a`](https://github.com/binimum/tidal-ui/commit/0c97c8a248083f3492f2bdaa4915ac57b5010451) Add downloads store and FFmpeg/track download banners ### 📊 Changes **5 files changed** (+557 additions, -76 deletions) <details> <summary>View changed files</summary> 📝 `src/lib/api.ts` (+50 -5) 📝 `src/lib/components/AudioPlayer.svelte` (+155 -53) 📝 `src/lib/components/TrackList.svelte` (+36 -9) 📝 `src/lib/ffmpegClient.ts` (+70 -9) ➕ `src/lib/stores/downloads.ts` (+246 -0) </details> ### 📄 Description ## Overview This PR enhances the AudioPlayer component with comprehensive download progress tracking, improved mobile layout, and better user control over downloads. All changes maintain backward compatibility and follow Svelte 5 best practices. ## Features Added ### 1. FFmpeg Download Banner with Countdown When FFmpeg WASM needs to be downloaded for metadata embedding, a banner now appears above the AudioPlayer with a 5-second countdown: - Shows "Downloading FFmpeg (32 MB) in X seconds..." with a cancellable countdown - Transitions to a progress bar during the actual download - Automatically skips the countdown when a user initiates a track download - Auto-dismisses after completion This gives users visibility into system-level operations and control over when FFmpeg loads. ### 2. Real-Time Track Download Progress Each downloading track now displays a dedicated progress banner above the AudioPlayer: - Shows album artwork, track title, and artist name - Real-time progress bar with percentage (0-99% for download, 99-100% for metadata processing) - Status text updates: "Downloading... X%" → "Processing metadata... X%" - Cancel button (X) to abort the download at any time - Multiple simultaneous downloads stack vertically Users can now monitor download progress and manage multiple downloads simultaneously. ### 3. Queue Panel Slide Animation The queue panel now smoothly slides in and out with a 300ms transition when opened or closed, providing a more polished user experience. ### 4. Mobile-Optimized Two-Line Layout The AudioPlayer now uses a responsive two-line layout on mobile devices (<640px): **Mobile:** - Line 1: Album cover + track info (title, artist, quality) - Line 2: Playback controls + queue button **Desktop (≥640px):** - Single-line layout with all elements visible including volume control This fixes the issue where buttons covered the track title on mobile devices. ### 5. Download Cancellation The download button in TrackList now shows an X icon while downloading (instead of a spinner). Clicking it immediately cancels the download: - Uses `AbortController` for clean fetch request cancellation - Progress banner disappears from the AudioPlayer - Button returns to the download icon state ## Technical Implementation ### New Downloads Store Created `src/lib/stores/downloads.ts` to centralize all download state management: - FFmpeg loading state (idle, countdown, loading, loaded, error) - Track download states with progress tracking - Derived stores for reactive UI updates - Methods for progress updates, cancellation, and cleanup ### Progress Tracking Enhanced `losslessAPI.downloadTrack()` to support progress callbacks using `ReadableStream`: ```typescript const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; received += value.length; onProgress(Math.round((received / total) * 100)); } ``` ### Clean Cancellation Downloads can be cancelled cleanly using `AbortController`: - Fetch requests are aborted mid-stream - Partial data is discarded - UI state updates to reflect cancellation - No memory leaks from pending operations ### Responsive Design Uses Tailwind CSS responsive classes for mobile/desktop layouts: - `flex-col` on mobile for stacked layout - `flex-row` on desktop for horizontal layout - `hidden sm:flex` pattern for mobile-hidden elements - Standard `sm:` breakpoint (640px) ## Files Changed **New:** - `src/lib/stores/downloads.ts` (235 lines) - Downloads state management **Modified:** - `src/lib/ffmpegClient.ts` (+75 lines) - Countdown and progress tracking - `src/lib/api.ts` (+45 lines) - Progress callbacks and AbortSignal support - `src/lib/components/AudioPlayer.svelte` (+120 lines) - Download banners, mobile layout, animations - `src/lib/components/TrackList.svelte` (+40 lines) - Cancellation support **Total:** ~515 lines added/modified ## Testing - ✅ TypeScript compilation: No errors - ✅ Svelte check: Passes (0 errors, 1 pre-existing warning) - ✅ Build: Succeeds - ✅ Uses Svelte 5 runes throughout - ✅ Follows existing code patterns Manual testing recommended for: - Visual appearance of download banners - Progress bar accuracy - Download cancellation - Mobile/desktop responsive behavior - Multiple simultaneous downloads ## Screenshots The implementation adds visual feedback for all download operations while maintaining the existing AudioPlayer design aesthetic. Download banners use the same dark theme with blue accents for system operations (FFmpeg) and gray for user actions (track downloads). Fixes #[issue-number] <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> 1. Can you add a little menu on top of the AudioPlayer that sticks up from it that says 'Downloading FFMpeg (xx MB) in 5 seconds...' with an X and then transitions into a progress bar? Also, if a user downloads a song, then skip the countdown. 2. Can you make the track downloading more verbose, like a progress bar with the percentage, like a attachment on top of AudioPlayer.svelte, the same as the FFMpeg thing with an X to cancel it? 3. Can you add a slide out/in animation to the queue opening? 4. On mobile, the buttons cover up the title, so can you make it two lines on mobile only, with album cover, title at the top and controls at the bottom? 5. Can you make it so that pressing the spinning circle on a downloading track stops the download and then shows an X to signify that? xt": "shoot " }, { "time": 228489, "duration": 632, "text": "across " }, { "time": 229121, "duration": 3519, "text": "the" } ], "element": { "key": "L55", "songPart": "Outro", "singer": "v2000" } }, { "time": 233064, "duration": 5111, "text": "That we shoot across the sky", "syllabus": [ { "time": 233064, "duration": 224, "text": "That " }, { "time": 233288, "duration": 216, "text": "we " }, { "time": 233504, "duration": 398, "text": "shoot " }, { "time": 233902, "duration": 600, "text": "across " }, { "time": 234502, "duration": 216, "text": "the " }, { "time": 234718, "duration": 3457, "text": "sky" } ], "element": { "key": "L56", "songPart": "Outro", "singer": "v2000" } }, { "time": 238296, "duration": 3638, "text": "That we shoot across the", "syllabus": [ { "time": 238296, "duration": 298, "text": "That " }, { "time": 238594, "duration": 152, "text": "we " }, { "time": 238746, "duration": 349, "text": "shoot " }, { "time": 239095, "duration": 651, "text": "across " }, { "time": 239746, "duration": 2188, "text": "the" } ], "element": { "key": "L57", "songPart": "Outro", "singer": "v2000" } }, { "time": 243701, "duration": 5323, "text": "That we shoot across the sky", "syllabus": [ { "time": 243701, "duration": 270, "text": "That " }, { "time": 243971, "duration": 200, "text": "we " }, { "time": 244171, "duration": 368, "text": "shoot " }, { "time": 244539, "duration": 667, "text": "across " }, { "time": 245206, "duration": 381, "text": "the " }, { "time": 245587, "duration": 3437, "text": "sky" } ], "element": { "key": "L58", "songPart": "Outro", "singer": "v2000" } }, { "time": 249395, "duration": 3154, "text": "That we shoot across the", "syllabus": [ { "time": 249395, "duration": 316, "text": "That " }, { ... </details> Created from VS Code via the [GitHub Pull Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) extension. <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/uimaxbai/tidal-ui/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-03-03 00:07:50 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tidal-ui#112
No description provided.