- feat(taglib): updated audio buffer handling in metadata.js to use Uint8Array.
- feat(taglib): refactored addMetadataToAudio to support return type as Blob or Uint8Array
- feat(taglib): add timeout functionality to metadata functions
- Introduced `withTimeout` utility function to handle operation timeouts.
- Updated `addMetadataWithTagLib` to use `withTimeout` for promise resolution.
- Updated `getMetadataWithTagLib` to use `withTimeout` for promise resolution.
- Added default timeout parameter to both metadata functions.
- feat(taglib): improve metadata handling with ChunkedByteVectorStream
- Enhanced metadata handling in taglib.ts and taglib.worker.ts to utilize ChunkedByteVectorStream.
- fix(taglib): handle metadata addition failure gracefully
- Updated `addMetadataWithTagLib` to catch errors and return original audio data if metadata addition fails.
fix(downloads): return original blob if metadata addition fails
- Wrap addMetadataToAudio call in try-catch to handle errors.
feat(taglib): add direct calling of taglib methods
- Introduced `direct` parameter to `addMetadataWithTagLib` and `getMetadataWithTagLib` functions for direct processing in the current thread.
- Exported taglib worker functions.
- Introduced `readableStreamIterator` to convert ReadableStream into async iterable.
- Updated `LosslessAPI` to utilize `readableStreamIterator` for handling response body.
- Modified `ZipNeutralinoWriter` to use `readableStreamIterator` for reading chunks.
- #322: Fullscreen overlay padding and main-view scrollable so volume stays above taskbar when Up next is shown
- #313: Settings tab content constrained on small displays with max-width, min-width, overflow-x
- #278: HEAD request before GET for download to get Content-Length for progress bar; resolveDownloadTotalBytes in downloadProgressUtils.js
- Improved progress tracking in FFmpeg worker by extracting total duration and current time from logs.
- Updated downloadTrackBlob function to use console logging for progress updates.
- Enhanced error handling and progress reporting during audio encoding.
taglib supports multiple media formats beyond what was previously supported, this would enable transcoding to other formats without needing to write additional metadata libraries.
Inline fixes:
- Remove TDRC frame from ID3 writer (ID3v2.3 uses TYER only, not TDRC)
- Add try/finally cleanup in worker to prevent VFS leaks on errors
- Fix Blob creation to use Uint8Array directly (avoid extra bytes)
- Replace loadFFmpeg race guard with promise singleton pattern
- Add -map_metadata -1 to strip source metadata (prevent duplicate ID3)
Error handling improvements:
- Create MP3EncodingError class with code property for reliable detection
- Update api.js to use instanceof check instead of string matching
- Pass AbortSignal to encodeToMp3 for proper cancellation support
- Remove error re-wrapping in mp3-encoder.js (preserve original errors)
Technical details:
- Promise singleton ensures FFmpeg loads once even with concurrent calls
- AbortSignal listener properly cleaned up on success/error/abort
- Virtual FS cleanup in finally block prevents file leaks
- MP3EncodingError.code = 'MP3_ENCODING_FAILED' for robust detection
Implements MP3 320kbps download functionality using ffmpeg.wasm for
industry-standard encoding with libmp3lame.
Features:
- New MP3_320 quality option in download settings UI
- ID3v2.3 metadata writing (title, artist, album, cover art, ISRC, etc.)
- Non-blocking encoding via Web Worker to keep UI responsive
- Proper UTF-16 with BOM text encoding for international characters
- Album artist fallback to track artist (mirrors FLAC/M4A behavior)
- Automatic format detection for downloaded audio
- Year validation to prevent writing NaN to ID3 tags
Implementation:
- mp3-encoder.js: Main encoder module with worker orchestration
- mp3-encoder.worker.js: FFmpeg Web Worker for async encoding
- id3-writer.js: ID3v2.3 tag writer with synchsafe size encoding
- Updates to api.js, metadata.js, utils.js for MP3 support
- Vite config excludes @ffmpeg packages from dep optimization
Technical details:
- Uses @ffmpeg/ffmpeg (libmp3lame 320kbps CBR, 44.1kHz)
- FFmpeg binary lazy-loaded from CDN (~25MB, cached)
- Encoding runs in separate thread (non-blocking UI)
- Proper error handling with distinct encoding vs network errors
- Memory-efficient: transfers ArrayBuffer with zero-copy
Dependencies:
- @ffmpeg/ffmpeg ^0.12.10
- @ffmpeg/util ^0.12.1
- Removed: package-lock.json (project uses bun.lock)
Closes maintainer request to use ffmpeg.wasm instead of lamejs.
Fixes#117
- Add getExtensionFromBlob() to detect format from blob signature
- DASH Hi-Res streams are MP4 containers, not raw FLAC
- Fix api.downloadTrack to detect and correct filename extension
- Fix bulk download functions to use detected extension
- Fallback to mime type if signature detection fails
- Update 'fetchWithRetry' in api.js to respect Retry-After header and use exponential backoff for 429 errors.
- Add 300ms delay between requests in CSV import loop to reduce load.
- Implements pagination in getPlaylist to support large playlists (>100 tracks).
- Reworks instance management to support separate api and streaming instance groups.
- Updates instances.json to the new grouped structure.
- Refactors apiSettings for smart incremental speed testing and type-aware caching.
- Parallelizes speed tests using appropriate endpoints (/artist/ for API, /track/ for Streaming).
- Updates Settings UI to display and manage both API and Streaming instance groups.
- Backfill album release date from tracks if missing in API response.
- Gracefully handle invalid or missing dates in UI rendering.
- Fix potential NaN in download folder naming due to invalid dates.