fix(download): estimate sizes from track quality metadata

This commit is contained in:
zarzet
2026-09-06 22:11:37 +07:00
parent ba00807786
commit 174852b056
16 changed files with 195 additions and 84 deletions
+40 -8
View File
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:spotiflac_android/l10n/app_localizations.dart';
import 'package:spotiflac_android/models/settings.dart';
import 'package:spotiflac_android/models/track.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/widgets/download_service_picker.dart';
@@ -56,6 +57,8 @@ class _PickerSettings extends SettingsNotifier {
Future<void> _openPicker(
WidgetTester tester, {
Duration? duration,
String? audioQuality = '16bit/44.1kHz',
String? source = 'provider-a',
AppSettings settings = const AppSettings(),
Locale locale = const Locale('en'),
double textScale = 1,
@@ -83,7 +86,17 @@ Future<void> _openPicker(
onPressed: () => DownloadServicePicker.show(
context,
trackName: 'Selected tracks',
duration: duration,
tracks: [
Track(
id: 'sample',
name: 'Sample',
artistName: 'Artist',
albumName: 'Album',
duration: duration?.inSeconds ?? 0,
source: source,
audioQuality: audioQuality,
),
],
onSelect: onSelect ?? (_, _) {},
),
child: const Text('Open'),
@@ -107,9 +120,7 @@ void main() {
duration: const Duration(minutes: 4),
onSelect: (quality, service) => selected = (quality, service),
);
expect(find.text('≈ 26.2 MB'), findsOneWidget);
expect(find.text('≈ 85.7 MB if 24-bit/96kHz'), findsOneWidget);
expect(find.text('≈ 171.4 MB if 24-bit/192kHz'), findsOneWidget);
expect(find.text('≈ 26.2 MB'), findsNWidgets(3));
expect(find.textContaining('MB'), findsNothing);
await tester.tap(find.text('Audio B'));
await tester.pumpAndSettle();
@@ -135,16 +146,37 @@ void main() {
},
);
testWidgets('capped estimates show their quality assumption in Indonesian', (
testWidgets('metadata caps both hi-res tiers to the available sample rate', (
tester,
) async {
await _openPicker(
tester,
duration: const Duration(minutes: 4),
audioQuality: '24bit/96kHz',
locale: const Locale('id'),
);
expect(find.text('≈ 85.7 MB jika 24-bit/96kHz'), findsOneWidget);
expect(find.text('171.4 MB jika 24-bit/192kHz'), findsOneWidget);
expect(find.text('≈ 85.7 MB'), findsNWidgets(2));
expect(find.text('26.2 MB'), findsOneWidget);
expect(find.textContaining('171.4'), findsNothing);
});
testWidgets('foreign or incomplete track quality stays unavailable', (
tester,
) async {
await _openPicker(
tester,
duration: const Duration(minutes: 4),
source: 'provider-b',
);
expect(find.text('Size estimate unavailable'), findsNWidgets(3));
await tester.pumpWidget(const SizedBox.shrink());
await tester.pumpAndSettle();
await _openPicker(
tester,
duration: const Duration(minutes: 4),
audioQuality: '24bit',
);
expect(find.text('Size estimate unavailable'), findsNWidgets(3));
});
testWidgets(
@@ -164,7 +196,7 @@ void main() {
autoConvertBitrate: '256k',
),
);
expect(find.text('≈ 26.2 MB'), findsOneWidget);
expect(find.text('≈ 26.2 MB'), findsNWidgets(3));
final conversionNote = find.textContaining(
'After conversion to OPUS: ≈ 7.3 MB',
);
+62 -13
View File
@@ -3,18 +3,76 @@ import 'package:spotiflac_android/models/track.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/utils/download_size_estimate.dart';
Track _track(int duration, {String? itemType}) => Track(
Track _track(
int duration, {
String? itemType,
String? audioQuality,
String? source = 'provider-a',
}) => Track(
id: 'track',
name: 'Track',
artistName: 'Artist',
albumName: 'Album',
duration: duration,
itemType: itemType,
audioQuality: audioQuality,
source: source,
);
void main() {
const duration = Duration(minutes: 4);
test('track metadata caps quality and sums each recording separately', () {
final cd = _track(240, audioQuality: '16bit/44.1kHz');
final hiRes = _track(120, audioQuality: '24-bit/96000Hz');
for (final id in ['LOSSLESS', 'HI_RES', 'HI_RES_LOSSLESS']) {
expect(
estimateDownloadSize(
duration: duration,
quality: QualityOption(id: id, label: ''),
tracks: [cd],
providerId: 'provider-a',
)!.bytes,
27518400,
);
}
final tracks = [cd, hiRes];
expect(
estimateDownloadSize(
duration: totalDownloadDuration(tracks),
quality: QualityOption.fromJson({
'id': 'best',
'label': 'Best',
'kind': 'lossless',
}),
tracks: tracks,
providerId: 'provider-a',
)!.bytes,
27518400 + 44928000,
);
});
test(
'lossless metadata must be complete and belong to selected provider',
() {
for (final track in [
_track(240),
_track(240, audioQuality: '24bit'),
_track(240, audioQuality: '16bit/44.1kHz', source: 'provider-b'),
]) {
expect(
estimateDownloadSize(
duration: duration,
quality: const QualityOption(id: 'LOSSLESS', label: ''),
tracks: [track],
providerId: 'provider-a',
),
isNull,
);
}
},
);
test('four minutes at 256 kbps is 7,680,000 bytes before overhead', () {
for (final quality in [
const QualityOption(id: 'opus_256', label: 'Opus'),
@@ -30,8 +88,6 @@ void main() {
quality: quality,
)!;
expect(estimate.bytes, 7680000);
expect(estimate.assumedBitDepth, isNull);
expect(estimate.assumedSampleRate, isNull);
}
});
@@ -39,16 +95,15 @@ void main() {
const cd = QualityOption(id: 'LOSSLESS', label: 'Lossless');
final cdSize = estimateDownloadSize(duration: duration, quality: cd)!;
expect(cdSize.bytes, 27518400);
expect(cdSize.assumedSampleRate, isNull);
final batchSize = estimateDownloadSize(
duration: totalDownloadDuration([_track(240), _track(240)]),
quality: cd,
)!;
expect(batchSize.bytes, cdSize.bytes * 2);
for (final (id, rate, bytes) in [
('HI_RES', 96000, 89856000),
('HI_RES_LOSSLESS', 192000, 179712000),
for (final (id, bytes) in [
('HI_RES', 89856000),
('HI_RES_LOSSLESS', 179712000),
]) {
final size = estimateDownloadSize(
duration: duration,
@@ -56,8 +111,6 @@ void main() {
)!;
expect(size.bytes, bytes);
expect(size.bytes, greaterThan(cdSize.bytes));
expect(size.assumedBitDepth, 24);
expect(size.assumedSampleRate, rate);
}
});
@@ -74,8 +127,6 @@ void main() {
quality: quality,
)!;
expect(estimate.bytes, 22464000);
expect(estimate.assumedBitDepth, isNull);
expect(estimate.assumedSampleRate, isNull);
final capped = estimateDownloadSize(
duration: duration,
quality: QualityOption.fromJson({
@@ -89,8 +140,6 @@ void main() {
}),
)!;
expect(capped.bytes, 179712000);
expect(capped.assumedBitDepth, 24);
expect(capped.assumedSampleRate, 192000);
},
);