Files
SpotiFLAC-Mobile/lib/services/conversion_library_service.dart
T

126 lines
4.0 KiB
Dart

import 'package:spotiflac_android/providers/download_history_provider.dart';
import 'package:spotiflac_android/services/history_database.dart';
import 'package:spotiflac_android/services/platform_bridge.dart';
import 'package:spotiflac_android/utils/audio_conversion_utils.dart';
class ConversionLibraryService {
const ConversionLibraryService._();
static Future<T?> _retryTransientSaf<T>(
Future<T?> Function() operation,
) async {
Object? lastError;
for (var attempt = 0; attempt < 2; attempt++) {
try {
final result = await operation();
if (result != null) return result;
} catch (error) {
lastError = error;
}
if (attempt == 0) {
await Future<void>.delayed(const Duration(milliseconds: 250));
}
}
if (lastError != null) throw lastError;
return null;
}
static Future<String?> copySafSourceToTemp(String uri) {
return _retryTransientSaf(() => PlatformBridge.copyContentUriToTemp(uri));
}
static Future<({String uri, String fileName})?> publishSafConversion({
required String treeUri,
required String relativeDir,
required String originalFileName,
required String targetFormat,
required String sourcePath,
required bool keepOriginal,
}) async {
final target = convertTargetExtAndMime(targetFormat);
final requestedName = convertedOutputFileName(
originalFileName: originalFileName,
targetFormat: targetFormat,
);
final replacesSameNamedDocument =
!keepOriginal &&
requestedName.toLowerCase() == originalFileName.toLowerCase();
if (!replacesSameNamedDocument) {
final result = await _retryTransientSaf(
() => PlatformBridge.createUniqueSafFileFromPath(
treeUri: treeUri,
relativeDir: relativeDir,
fileName: requestedName,
mimeType: target.mime,
srcPath: sourcePath,
),
);
if (result == null) return null;
final uri = (result['uri'] as String? ?? '').trim();
final fileName = (result['file_name'] as String? ?? '').trim();
if (uri.isEmpty || fileName.isEmpty) return null;
return (uri: uri, fileName: fileName);
}
final uri = await _retryTransientSaf(
() => PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
relativeDir: relativeDir,
fileName: requestedName,
mimeType: target.mime,
srcPath: sourcePath,
),
);
if (uri == null || uri.trim().isEmpty) return null;
return (uri: uri, fileName: requestedName);
}
static Future<void> persistHistoryConversion({
required DownloadHistoryItem source,
required String newFilePath,
required String newQuality,
required String targetFormat,
required String bitrate,
required int? bitDepth,
required int? sampleRate,
required bool keepOriginal,
String? newSafFileName,
}) async {
final convertedBitrate = convertedAudioBitrateKbps(
targetFormat: targetFormat,
bitrate: bitrate,
);
final isLossless = isLosslessConversionTarget(targetFormat);
if (!keepOriginal) {
await HistoryDatabase.instance.updateFilePath(
source.id,
newFilePath,
newSafFileName: newSafFileName,
newQuality: newQuality,
newFormat: normalizedConvertedAudioFormat(targetFormat),
newBitrate: convertedBitrate,
newBitDepth: bitDepth,
newSampleRate: sampleRate,
clearAudioSpecs: !isLossless,
);
return;
}
final converted = source.toJson()
..['id'] = convertedLibraryItemId(source.id, newFilePath)
..['filePath'] = newFilePath
..['downloadedAt'] = DateTime.now().toIso8601String()
..['quality'] = newQuality
..['format'] = normalizedConvertedAudioFormat(targetFormat)
..['bitrate'] = convertedBitrate
..['bitDepth'] = isLossless ? bitDepth : null
..['sampleRate'] = isLossless ? sampleRate : null;
if (newSafFileName != null) {
converted['safFileName'] = newSafFileName;
}
await HistoryDatabase.instance.upsert(converted);
}
}