mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-23 10:00:49 +02:00
refactor(providers): split download history out of download_queue_provider
download_queue_provider.dart (9,525 lines) hosted the entire download history feature alongside the queue. Move DownloadHistoryItem/State/ Notifier and the history providers to download_history_provider.dart, and promote the audio format/quality helpers both need into lib/utils/audio_format_utils.dart. The queue provider re-exports the history library, so existing imports keep working unchanged.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,150 @@
|
||||
import 'package:spotiflac_android/utils/int_utils.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
|
||||
/// Audio format/quality helpers shared by the download queue and history
|
||||
/// providers.
|
||||
int? readPositiveBitrateKbps(dynamic value) {
|
||||
final parsed = readPositiveInt(value);
|
||||
if (parsed == null) return null;
|
||||
final kbps = parsed >= 10000 ? (parsed / 1000).round() : parsed;
|
||||
return kbps >= 16 ? kbps : null;
|
||||
}
|
||||
|
||||
String? audioFormatForPath(String? filePath, {String? fileName}) {
|
||||
final candidates = <String>[?filePath, ?fileName];
|
||||
for (final candidate in candidates) {
|
||||
final lower = candidate.trim().toLowerCase();
|
||||
if (lower.endsWith('.opus') || lower.endsWith('.ogg')) return 'OPUS';
|
||||
if (lower.endsWith('.mp3')) return 'MP3';
|
||||
if (lower.endsWith('.aac')) return 'AAC';
|
||||
if (lower.endsWith('.m4a') || lower.endsWith('.mp4')) return 'M4A';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? nonPlaceholderQuality(String? quality) {
|
||||
final normalized = normalizeOptionalString(quality);
|
||||
if (normalized == null || isPlaceholderQualityLabel(normalized)) {
|
||||
return null;
|
||||
}
|
||||
final bitrateMatch = RegExp(
|
||||
r'\b(\d+)\s*kbps\b',
|
||||
caseSensitive: false,
|
||||
).firstMatch(normalized);
|
||||
if (bitrateMatch != null) {
|
||||
final bitrate = int.tryParse(bitrateMatch.group(1) ?? '');
|
||||
if (bitrate != null && bitrate < 16) return null;
|
||||
}
|
||||
final lower = normalized.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), '_');
|
||||
const requestedLosslessLabels = {
|
||||
'hi_res_lossless',
|
||||
'hires_lossless',
|
||||
'hi_res',
|
||||
'hires',
|
||||
'flac_best_available',
|
||||
};
|
||||
if (requestedLosslessLabels.contains(lower)) return null;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
String? normalizeAudioFormatValue(String? value) {
|
||||
final normalized = normalizeOptionalString(
|
||||
value,
|
||||
)?.toLowerCase().replaceAll('-', '_');
|
||||
return switch (normalized) {
|
||||
'flac' => 'flac',
|
||||
'alac' => 'alac',
|
||||
'aac' || 'mp4a' => 'aac',
|
||||
'eac3' || 'ec_3' => 'eac3',
|
||||
'ac3' || 'ac_3' => 'ac3',
|
||||
'ac4' || 'ac_4' => 'ac4',
|
||||
'mp3' => 'mp3',
|
||||
'opus' || 'ogg' => 'opus',
|
||||
'm4a' || 'mp4' => 'm4a',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
bool isLossyAudioFormat(String? value) {
|
||||
return const {
|
||||
'aac',
|
||||
'eac3',
|
||||
'ac3',
|
||||
'ac4',
|
||||
'mp3',
|
||||
'opus',
|
||||
'm4a',
|
||||
}.contains(normalizeAudioFormatValue(value));
|
||||
}
|
||||
|
||||
String lossyFormatForSetting(String value) {
|
||||
final normalized = value.trim().toLowerCase();
|
||||
if (normalized.startsWith('opus')) return 'opus';
|
||||
if (normalized.startsWith('aac') || normalized.startsWith('m4a')) {
|
||||
return 'aac';
|
||||
}
|
||||
return 'mp3';
|
||||
}
|
||||
|
||||
String lossyExtensionForFormat(String format) {
|
||||
return switch (format) {
|
||||
'opus' => '.opus',
|
||||
'aac' => '.m4a',
|
||||
_ => '.mp3',
|
||||
};
|
||||
}
|
||||
|
||||
String metadataFormatForLossyFormat(String format) {
|
||||
return format == 'aac' ? 'm4a' : format;
|
||||
}
|
||||
|
||||
String displayFormatForLossyFormat(String format) {
|
||||
return format == 'aac' ? 'AAC' : format.toUpperCase();
|
||||
}
|
||||
|
||||
String? resolveDisplayQuality({
|
||||
required String? filePath,
|
||||
String? fileName,
|
||||
String? detectedFormat,
|
||||
int? bitDepth,
|
||||
int? sampleRate,
|
||||
int? bitrateKbps,
|
||||
String? storedQuality,
|
||||
}) {
|
||||
final format =
|
||||
displayFormatForCodec(detectedFormat) ??
|
||||
audioFormatForPath(filePath, fileName: fileName);
|
||||
if (format == 'OPUS' ||
|
||||
format == 'MP3' ||
|
||||
format == 'AAC' ||
|
||||
format == 'EAC3' ||
|
||||
format == 'AC3' ||
|
||||
format == 'AC4' ||
|
||||
(format == 'M4A' && (bitDepth == null || bitDepth <= 0))) {
|
||||
return buildDisplayAudioQuality(bitrateKbps: bitrateKbps, format: format) ??
|
||||
nonPlaceholderQuality(storedQuality) ??
|
||||
format;
|
||||
}
|
||||
return buildDisplayAudioQuality(
|
||||
bitDepth: bitDepth,
|
||||
sampleRate: sampleRate,
|
||||
storedQuality: nonPlaceholderQuality(storedQuality) ?? storedQuality,
|
||||
);
|
||||
}
|
||||
|
||||
String? displayFormatForCodec(String? value) {
|
||||
final normalized = normalizeOptionalString(
|
||||
value,
|
||||
)?.toLowerCase().replaceAll('-', '_');
|
||||
return switch (normalized) {
|
||||
'flac' => 'FLAC',
|
||||
'alac' => 'ALAC',
|
||||
'aac' || 'mp4a' => 'AAC',
|
||||
'eac3' || 'ec_3' => 'EAC3',
|
||||
'ac3' || 'ac_3' => 'AC3',
|
||||
'ac4' || 'ac_4' => 'AC4',
|
||||
'mp3' => 'MP3',
|
||||
'opus' => 'OPUS',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user