feat: add local backup export import core
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@ -7,7 +8,8 @@ import 'package:path_provider/path_provider.dart';
|
||||
import '../../application/application.dart';
|
||||
import '../../domain/domain.dart';
|
||||
|
||||
final class PathProviderLocalMediaStorage implements LocalMediaStorage {
|
||||
final class PathProviderLocalMediaStorage
|
||||
implements LocalMediaStorage, LocalBackupMediaStore {
|
||||
const PathProviderLocalMediaStorage();
|
||||
|
||||
static const _mediaDirectoryName = 'gametime_media';
|
||||
@ -77,6 +79,64 @@ final class PathProviderLocalMediaStorage implements LocalMediaStorage {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<EmbeddedBackupMediaFile>> readEmbeddableFiles(
|
||||
List<LocalBackupResource> mediaAssets,
|
||||
) async {
|
||||
final files = <EmbeddedBackupMediaFile>[];
|
||||
for (final resource in mediaAssets) {
|
||||
final localUri = resource.payload['localUri'];
|
||||
if (localUri is! String || localUri.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
final file = _fileFromLocalUri(localUri);
|
||||
if (!await file.exists()) {
|
||||
continue;
|
||||
}
|
||||
final bytes = await file.readAsBytes();
|
||||
files.add(
|
||||
EmbeddedBackupMediaFile(
|
||||
mediaAssetId: resource.id,
|
||||
role: 'original',
|
||||
fileName: p.basename(file.path),
|
||||
mimeType: resource.payload['mimeType'] as String?,
|
||||
sizeBytes: bytes.length,
|
||||
base64: base64Encode(bytes),
|
||||
),
|
||||
);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, RestoredMediaFile>> restoreEmbeddedFiles(
|
||||
List<EmbeddedBackupMediaFile> files,
|
||||
) async {
|
||||
final restored = <String, RestoredMediaFile>{};
|
||||
final directory = await _restoredBackupDirectory();
|
||||
await directory.create(recursive: true);
|
||||
for (final file in files) {
|
||||
final bytes = switch (_decodeBase64OrNull(file.base64)) {
|
||||
final decoded? => decoded,
|
||||
null => null,
|
||||
};
|
||||
if (bytes == null) {
|
||||
continue;
|
||||
}
|
||||
final extension = p.extension(file.fileName).toLowerCase();
|
||||
final target = File(
|
||||
p.join(directory.path, '${file.mediaAssetId}$extension'),
|
||||
);
|
||||
await target.writeAsBytes(bytes, flush: true);
|
||||
restored[file.mediaAssetId] = RestoredMediaFile(
|
||||
mediaAssetId: file.mediaAssetId,
|
||||
localUri: target.uri.toString(),
|
||||
sizeBytes: bytes.length,
|
||||
);
|
||||
}
|
||||
return restored;
|
||||
}
|
||||
|
||||
Future<Directory> _kindDirectory(MediaKind kind) async {
|
||||
final root = await _rootDirectory();
|
||||
final child = switch (kind) {
|
||||
@ -90,6 +150,27 @@ final class PathProviderLocalMediaStorage implements LocalMediaStorage {
|
||||
final documents = await getApplicationDocumentsDirectory();
|
||||
return Directory(p.join(documents.path, _mediaDirectoryName));
|
||||
}
|
||||
|
||||
Future<Directory> _restoredBackupDirectory() async {
|
||||
final root = await _rootDirectory();
|
||||
return Directory(p.join(root.path, 'restored'));
|
||||
}
|
||||
}
|
||||
|
||||
File _fileFromLocalUri(String localUri) {
|
||||
final uri = Uri.tryParse(localUri);
|
||||
if (uri != null && uri.scheme == 'file') {
|
||||
return File.fromUri(uri);
|
||||
}
|
||||
return File(localUri);
|
||||
}
|
||||
|
||||
Uint8List? _decodeBase64OrNull(String value) {
|
||||
try {
|
||||
return base64Decode(value);
|
||||
} on FormatException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final class _ImageDimensions {
|
||||
|
||||
Reference in New Issue
Block a user