mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-20 08:42:16 +02:00
feat(download): estimate file sizes in quality picker
This commit is contained in:
@@ -6,11 +6,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/utils/audio_format_utils.dart';
|
||||
import 'package:spotiflac_android/utils/download_size_estimate.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
|
||||
class DownloadServicePicker extends ConsumerStatefulWidget {
|
||||
final String? trackName;
|
||||
final String? artistName;
|
||||
final String? coverUrl;
|
||||
|
||||
/// Combined duration for the tracks being queued; null if any is unknown.
|
||||
final Duration? duration;
|
||||
final void Function(String quality, String service) onSelect;
|
||||
final String? recommendedService;
|
||||
|
||||
@@ -19,6 +25,7 @@ class DownloadServicePicker extends ConsumerStatefulWidget {
|
||||
this.trackName,
|
||||
this.artistName,
|
||||
this.coverUrl,
|
||||
this.duration,
|
||||
required this.onSelect,
|
||||
this.recommendedService,
|
||||
});
|
||||
@@ -32,6 +39,7 @@ class DownloadServicePicker extends ConsumerStatefulWidget {
|
||||
String? trackName,
|
||||
String? artistName,
|
||||
String? coverUrl,
|
||||
Duration? duration,
|
||||
String? recommendedService,
|
||||
required void Function(String quality, String service) onSelect,
|
||||
}) {
|
||||
@@ -49,6 +57,7 @@ class DownloadServicePicker extends ConsumerStatefulWidget {
|
||||
trackName: trackName,
|
||||
artistName: artistName,
|
||||
coverUrl: coverUrl,
|
||||
duration: duration,
|
||||
onSelect: onSelect,
|
||||
recommendedService: recommendedService,
|
||||
),
|
||||
@@ -120,6 +129,21 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
final downloadExtensions = _downloadExtensions();
|
||||
final hasProviders = downloadExtensions.isNotEmpty;
|
||||
final qualityOptions = _getQualityOptions(downloadExtensions);
|
||||
final settings = ref.watch(settingsProvider);
|
||||
final convertedSize = settings.autoConvertDownloads
|
||||
? estimateDownloadSize(
|
||||
duration: widget.duration,
|
||||
quality: QualityOption(
|
||||
id: 'converted',
|
||||
label: '',
|
||||
sizeEstimate: QualitySizeEstimate(
|
||||
bitrateKbps: autoConvertBitrateKbps(
|
||||
settings.autoConvertBitrate,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null;
|
||||
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
@@ -191,12 +215,40 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
_QualityOption(
|
||||
title: _localizedQualityLabel(context, quality),
|
||||
subtitle: _localizedQualityDescription(context, quality),
|
||||
estimatedSize: _sizeLabel(
|
||||
context,
|
||||
estimateDownloadSize(
|
||||
duration: widget.duration,
|
||||
quality: quality,
|
||||
),
|
||||
),
|
||||
icon: _getQualityIcon(quality.id),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
widget.onSelect(quality.id, _selectedService);
|
||||
},
|
||||
),
|
||||
if (qualityOptions.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 8, 24, 0),
|
||||
child: Text(
|
||||
[
|
||||
context.l10n.downloadSizeEstimateNote,
|
||||
if (convertedSize != null)
|
||||
context.l10n.downloadConvertedSizeEstimate(
|
||||
displayFormatForLossyFormat(
|
||||
normalizeAutoConvertFormat(
|
||||
settings.autoConvertFormat,
|
||||
),
|
||||
),
|
||||
_sizeLabel(context, convertedSize),
|
||||
),
|
||||
].join('\n\n'),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
const SizedBox(height: 16),
|
||||
@@ -206,6 +258,14 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
);
|
||||
}
|
||||
|
||||
String _sizeLabel(BuildContext context, DownloadSizeEstimate? estimate) {
|
||||
if (estimate == null) return context.l10n.downloadSizeUnavailable;
|
||||
final size = estimate.minBytes == estimate.maxBytes
|
||||
? formatBytes(estimate.maxBytes)
|
||||
: '${formatBytes(estimate.minBytes)}–${formatBytes(estimate.maxBytes)}';
|
||||
return context.l10n.downloadEstimatedSize(size);
|
||||
}
|
||||
|
||||
IconData _getQualityIcon(String qualityId) {
|
||||
final normalized = qualityId.toUpperCase();
|
||||
if (normalized.startsWith('MP3_') || normalized == 'MP3') {
|
||||
@@ -260,12 +320,14 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
|
||||
class _QualityOption extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String estimatedSize;
|
||||
final IconData icon;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _QualityOption({
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.estimatedSize,
|
||||
required this.icon,
|
||||
required this.onTap,
|
||||
});
|
||||
@@ -284,12 +346,20 @@ class _QualityOption extends StatelessWidget {
|
||||
child: Icon(icon, color: colorScheme.onPrimaryContainer, size: 20),
|
||||
),
|
||||
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
subtitle: subtitle.isNotEmpty
|
||||
? Text(
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (subtitle.isNotEmpty)
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
Text(
|
||||
estimatedSize,
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/library_collections_provider.dart';
|
||||
import 'package:spotiflac_android/providers/local_library_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/utils/download_size_estimate.dart';
|
||||
import 'package:spotiflac_android/utils/logger.dart';
|
||||
import 'package:spotiflac_android/widgets/download_service_picker.dart';
|
||||
import 'package:spotiflac_android/widgets/view_queue_snackbar_action.dart';
|
||||
@@ -35,6 +36,7 @@ void downloadSingleTrack(
|
||||
trackName: track.name,
|
||||
artistName: track.artistName,
|
||||
coverUrl: track.coverUrl,
|
||||
duration: Duration(seconds: track.duration),
|
||||
recommendedService: recommendedService,
|
||||
onSelect: (quality, service) {
|
||||
ref
|
||||
@@ -218,6 +220,7 @@ Future<void> queueTracksSkippingDownloaded(
|
||||
context,
|
||||
trackName: '${tracksToQueue.length} tracks',
|
||||
artistName: artistNameForPicker,
|
||||
duration: totalDownloadDuration(tracksToQueue),
|
||||
recommendedService: recommendedService,
|
||||
onSelect: (quality, service) {
|
||||
ref
|
||||
|
||||
Reference in New Issue
Block a user