mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-02 17:18:36 +02:00
253 lines
7.9 KiB
Dart
253 lines
7.9 KiB
Dart
part of 'home_tab.dart';
|
|
|
|
extension _HomeTabCsvImport on _HomeTabState {
|
|
Future<void> _importCsv(BuildContext context, WidgetRef ref) async {
|
|
if (_isCsvImporting) return;
|
|
_setCsvImporting(true);
|
|
|
|
int currentProgress = 0;
|
|
int totalTracks = 0;
|
|
|
|
bool progressDialogInitialized = false;
|
|
bool progressDialogVisible = false;
|
|
BuildContext? progressDialogContext;
|
|
StateSetter? setDialogState;
|
|
|
|
void showProgressDialog() {
|
|
if (progressDialogInitialized || !mounted) return;
|
|
progressDialogInitialized = true;
|
|
progressDialogVisible = true;
|
|
showDialog<void>(
|
|
context: this.context,
|
|
useRootNavigator: false,
|
|
barrierDismissible: false,
|
|
builder: (dialogCtx) => StatefulBuilder(
|
|
builder: (dialogCtx, setState) {
|
|
progressDialogContext = dialogCtx;
|
|
setDialogState = setState;
|
|
return AlertDialog(
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const CircularProgressIndicator(),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
totalTracks > 0
|
|
? context.l10n.progressFetchingMetadata(
|
|
currentProgress,
|
|
totalTracks,
|
|
)
|
|
: context.l10n.progressReadingCsv,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
).then((_) {
|
|
progressDialogVisible = false;
|
|
progressDialogContext = null;
|
|
});
|
|
}
|
|
|
|
void closeProgressDialog() {
|
|
if (!progressDialogVisible) return;
|
|
setDialogState = null;
|
|
try {
|
|
if (progressDialogContext != null) {
|
|
Navigator.of(progressDialogContext!).pop();
|
|
} else if (mounted) {
|
|
final navigator = Navigator.of(this.context);
|
|
if (navigator.canPop()) {
|
|
navigator.pop();
|
|
}
|
|
}
|
|
} catch (_) {}
|
|
progressDialogVisible = false;
|
|
progressDialogContext = null;
|
|
}
|
|
|
|
try {
|
|
final tracks = await CsvImportService.pickAndParseCsv(
|
|
onProgress: (current, total) {
|
|
currentProgress = current;
|
|
totalTracks = total;
|
|
if (!progressDialogInitialized && total > 0) {
|
|
showProgressDialog();
|
|
}
|
|
setDialogState?.call(() {});
|
|
},
|
|
);
|
|
|
|
closeProgressDialog();
|
|
|
|
if (tracks.isNotEmpty) {
|
|
final settings = ref.read(settingsProvider);
|
|
|
|
if (!mounted) return;
|
|
|
|
final l10n = this.context.l10n;
|
|
|
|
final options = await showDialog<_CsvImportOptions>(
|
|
context: this.context,
|
|
useRootNavigator: false,
|
|
builder: (dialogCtx) {
|
|
var skipDownloaded = true;
|
|
return StatefulBuilder(
|
|
builder: (dialogCtx, setDialogState) => AlertDialog(
|
|
title: Text(l10n.dialogImportPlaylistTitle),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l10n.dialogImportPlaylistMessage(tracks.length)),
|
|
const SizedBox(height: 12),
|
|
CheckboxListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
title: Text(l10n.homeSkipAlreadyDownloaded),
|
|
value: skipDownloaded,
|
|
onChanged: (value) {
|
|
setDialogState(() {
|
|
skipDownloaded = value ?? true;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(
|
|
dialogCtx,
|
|
const _CsvImportOptions(
|
|
confirmed: false,
|
|
skipDownloaded: true,
|
|
),
|
|
),
|
|
child: Text(l10n.dialogCancel),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.pop(
|
|
dialogCtx,
|
|
_CsvImportOptions(
|
|
confirmed: true,
|
|
skipDownloaded: skipDownloaded,
|
|
),
|
|
),
|
|
child: Text(l10n.dialogImport),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
|
|
if (options == null || !options.confirmed) return;
|
|
|
|
var tracksToQueue = tracks;
|
|
var skippedDownloadedCount = 0;
|
|
|
|
if (options.skipDownloaded) {
|
|
final historyLookups = tracks
|
|
.map(historyLookupForTrack)
|
|
.toList(growable: false);
|
|
final existingHistoryKeys = await ref.read(
|
|
downloadHistoryBatchExistsProvider(
|
|
HistoryBatchLookupRequest(historyLookups),
|
|
).future,
|
|
);
|
|
tracksToQueue = [];
|
|
for (var i = 0; i < tracks.length; i++) {
|
|
final track = tracks[i];
|
|
final isDownloaded = existingHistoryKeys.contains(
|
|
historyLookups[i].lookupKey,
|
|
);
|
|
if (isDownloaded) {
|
|
skippedDownloadedCount++;
|
|
continue;
|
|
}
|
|
tracksToQueue.add(track);
|
|
}
|
|
}
|
|
|
|
if (tracksToQueue.isEmpty) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(this.context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
l10n.discographySkippedDownloaded(0, skippedDownloadedCount),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
final queueSnackbarMessage = skippedDownloadedCount > 0
|
|
? l10n.discographySkippedDownloaded(
|
|
tracksToQueue.length,
|
|
skippedDownloadedCount,
|
|
)
|
|
: l10n.snackbarAddedTracksToQueue(tracksToQueue.length);
|
|
|
|
if (!mounted) return;
|
|
|
|
if (settings.askQualityBeforeDownload ||
|
|
settings.allowQualityVariants) {
|
|
DownloadServicePicker.show(
|
|
this.context,
|
|
trackName: l10n.csvImportTracks(tracksToQueue.length),
|
|
artistName: l10n.dialogImportPlaylistTitle,
|
|
onSelect: (quality, service) {
|
|
ref
|
|
.read(downloadQueueProvider.notifier)
|
|
.addMultipleToQueue(
|
|
tracksToQueue,
|
|
service,
|
|
qualityOverride: quality,
|
|
);
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(this.context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(queueSnackbarMessage),
|
|
action: buildViewQueueSnackBarAction(this.context),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
} else {
|
|
final extensionState = ref.read(extensionProvider);
|
|
final service = resolveEffectiveDownloadService(
|
|
settings.defaultService,
|
|
extensionState,
|
|
);
|
|
if (service.isEmpty) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(this.context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(this.context.l10n.extensionsNoDownloadProvider),
|
|
),
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
ref
|
|
.read(downloadQueueProvider.notifier)
|
|
.addMultipleToQueue(tracksToQueue, service);
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(this.context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(queueSnackbarMessage),
|
|
action: buildViewQueueSnackBarAction(this.context),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
closeProgressDialog();
|
|
_setCsvImporting(false);
|
|
}
|
|
}
|
|
}
|