From e761dd604f427a2fb3d6bd8887f66fb2c404aa15 Mon Sep 17 00:00:00 2001 From: zarzet Date: Sat, 1 Aug 2026 19:39:09 +0700 Subject: [PATCH] docs(release): add 4.8.5 changelog --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++ lib/widgets/settings_group.dart | 28 ++++---------------- test/settings_search_test.dart | 13 ++++++--- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faac585f..42603d30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,52 @@ # Changelog +## [4.8.5] - 2026-08-01 + +### Added + +- **Searchable Settings**: Search both top-level pages and controls inside them. Opening a nested result now scrolls to the matching control and briefly highlights it. +- **Album Multi-Select Download**: Select several tracks in an album and choose the provider and quality once for the whole selection. +- **Player & Queue Controls**: Repeat Off/All/One modes, swipe up from Now Playing to open the queue, move queued tracks up or down, `Download next`, and one-tap retry when connectivity returns. +- **Playback Session Restore**: Restore the previous queue, track, technical quality, and playback position after the app process is restarted. +- **Track Navigation**: `Go to Album` is available from track actions, including sparse search results, and `Open on...` exposes supported platform links. +- **Library Views**: Playlist filtering, a configurable default Library view, a missing-ISRC filter, and clearer horizontally scrollable filter chips. +- **Quality Labels**: Choose between measured bitrate labels and classic bit-depth/sample-rate labels. High-bitrate tracks receive a distinct badge color, and lossless average bitrate is shown in Track Metadata. +- **Duplicate Review**: Detect ISRC duplicates beyond FLAC, review matching files, and keep the preferred copy. +- **M3U Playlists**: Import M3U/M3U8 playlists and export Library collections as M3U8. +- **Metadata Sources**: Choose a specific extension for online auto-fill, use extension-defined custom search, or manually fetch metadata from MusicBrainz. +- **Cover Tools**: View the current embedded-cover dimensions, preview the dimensions of a fetched cover, resize embedded artwork, and select the best available online cover candidate. +- **Android Download Widget**: Monitor the active download queue from the Android home screen. +- **Appearance Controls**: Adaptive detail-page colors derived from cover art and an optional force-backdrop-blur setting for lower device tiers. +- **Audio Processing**: Very-high-quality SoXR resampling settings for supported lossless conversions. + +### Fixed + +- **Provider Matching**: Honor the explicitly selected provider and reject mismatched provider results that could download a cover or unrelated song with the original album metadata. +- **Verification & Sessions**: Coordinate one verification flow across sequential and parallel album downloads, follow canonical gateway/provider error contracts, block challenged session generations, and prevent stale requests from invalidating a newer session. +- **Verification Alerts**: Notify the user when downloads pause for verification and recover signed-session bootstrap failures more reliably. +- **Download Storage**: Recover from an unwritable selected folder, retain published files when bookkeeping fails, and keep cancelled items retryable after app restarts. +- **Quality Variants**: Complete alternate-quality downloads reliably and append technical quality to filenames only when a real filename collision exists. +- **Library Stability**: Keep rows and cached history visible during active downloads and refreshes, retain records when deletion fails, and backfill/persist local bitrate metadata without opening every track. +- **Playback & Lyrics**: Refresh lyrics after automatic track changes and cold starts, reject empty embedded lyric payloads, preserve technical quality between tracks, and restore playback position reliably. +- **WAV & AIFF Metadata**: Preserve supported tags and artwork through conversion and metadata editing. +- **Audio Analysis**: Wait for complete FFmpeg metrics, isolate analysis from active downloads, detect effective/full-band spectral cutoff more accurately, and avoid unsupported AC-4 spectrogram analysis. +- **Startup Compatibility**: Stop forcing the highest Android display mode during every cold start, avoiding OEM-specific crashes and unnecessary GPU load. +- **User-Facing Errors**: Replace raw `PlatformException` and backend details with concise, sanitized messages. +- **Metadata UI**: Keep Track Metadata headers readable in light themes and make the online source picker, preview, apply, and feedback flow consistent with the rest of the app. +- **Selection & Sheets**: Keep selection bars and modal sheets above the app navbar and make download-picker drag dismissal feel natural. +- **Database Upgrade**: Make playback-session migration safe when an older installation already contains the destination table. + +### Improved + +- **Now Playing**: Smoother track transitions, consistent custom action sheets, better synced-lyrics positioning, and aligned play/preview controls and hitboxes. +- **Responsive UI**: More consistent search fields, settings groups, page headers, modal sheets, empty/error states, and collection selection behavior, with improved tablet widths and short-screen setup/tutorial layouts. +- **Download Performance**: Faster duplicate checks for large playlists, fewer native-widget updates, reduced progress/session write churn, and safer HTTP Range recovery. +- **Library Performance**: Batched history/index updates, fewer rebuilds during scans, lazy duplicate loading, cached MusicBrainz lookups, and quality probes that avoid unnecessary SAF copies. +- **Extension Security**: Verify decentralized package integrity, isolate bad registry checksums, anchor URL handlers to their declared hosts, and enforce `minAppVersion` and required runtime features. +- **Dependencies**: Updated Flutter, Go, FFmpeg, Android notification, and supporting native dependencies. + +--- + ## [4.8.0] - 2026-07-24 ### Added diff --git a/lib/widgets/settings_group.dart b/lib/widgets/settings_group.dart index ad667282..6a5ad2fc 100644 --- a/lib/widgets/settings_group.dart +++ b/lib/widgets/settings_group.dart @@ -146,7 +146,7 @@ class _SettingsSearchTargetState extends State { ); if (!mounted) return; - _highlightTimer = Timer(const Duration(milliseconds: 1800), () { + _highlightTimer = Timer(const Duration(milliseconds: 1400), () { if (mounted) setState(() => _highlighted = false); }); } @@ -168,29 +168,11 @@ class _SettingsSearchTargetState extends State { selected: _highlighted, child: AnimatedContainer( key: ValueKey('settings-highlight:${widget.label}'), - duration: const Duration(milliseconds: 320), + duration: context.tokens.motionMedium, curve: Curves.easeOutCubic, - decoration: BoxDecoration( - color: _highlighted - ? colorScheme.primaryContainer.withValues(alpha: 0.72) - : Colors.transparent, - borderRadius: BorderRadius.circular(context.tokens.radiusCover), - border: _highlighted - ? Border.all( - color: colorScheme.primary.withValues(alpha: 0.72), - width: 1.5, - ) - : null, - boxShadow: _highlighted - ? [ - BoxShadow( - color: colorScheme.primary.withValues(alpha: 0.28), - blurRadius: 18, - spreadRadius: 2, - ), - ] - : const [], - ), + color: _highlighted + ? colorScheme.primaryContainer.withValues(alpha: 0.3) + : Colors.transparent, child: widget.child, ), ); diff --git a/test/settings_search_test.dart b/test/settings_search_test.dart index 9b8cc042..c2af962b 100644 --- a/test/settings_search_test.dart +++ b/test/settings_search_test.dart @@ -69,7 +69,7 @@ void main() { }, ); - testWidgets('search target scrolls into view and briefly glows', ( + testWidgets('search target scrolls into view with standard selected color', ( tester, ) async { final scrollController = ScrollController(); @@ -105,10 +105,15 @@ void main() { ); final highlighted = tester.widget(highlightFinder); final highlightedDecoration = highlighted.decoration! as BoxDecoration; - expect(highlightedDecoration.color, isNot(Colors.transparent)); - expect(highlightedDecoration.boxShadow, isNotEmpty); + final colorScheme = Theme.of(tester.element(highlightFinder)).colorScheme; + expect( + highlightedDecoration.color, + colorScheme.primaryContainer.withValues(alpha: 0.3), + ); + expect(highlightedDecoration.border, isNull); + expect(highlightedDecoration.boxShadow, isNull); - await tester.pump(const Duration(milliseconds: 2300)); + await tester.pump(const Duration(milliseconds: 1900)); await tester.pump(const Duration(milliseconds: 400)); final faded = tester.widget(highlightFinder); final fadedDecoration = faded.decoration! as BoxDecoration;