mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-13 19:21:23 +02:00
feat(backup): include installed extensions and settings in backup/restore
Back up the store registry URL plus each installed extension (id, version, enabled flag and settings) and restore them on a new device by reinstalling from the store and re-applying settings. Secret-flagged settings (tokens/API keys) are excluded by default behind an opt-in 'Include extension credentials' toggle. Device-bound signed sessions are never backed up. Settings are merged on restore so omitted secrets are not wiped; failed reinstalls are reported.
This commit is contained in:
1 parent
b2705cb2ae
commit
1787059f42
21 files changed
+989
-5
No files matched your search
@@ -25,6 +25,9 @@ class BackupBundle {
|
||||
/// Playlist cover images keyed by playlist id: `{ id: { ext, data } }`.
|
||||
final Map<String, dynamic> playlistCovers;
|
||||
|
||||
/// Extensions section: `{ registry_url, items: [ {id, version, enabled, settings} ] }`.
|
||||
final Map<String, dynamic> extensions;
|
||||
|
||||
const BackupBundle({
|
||||
required this.formatVersion,
|
||||
required this.appVersion,
|
||||
@@ -33,6 +36,7 @@ class BackupBundle {
|
||||
required this.history,
|
||||
required this.collections,
|
||||
required this.playlistCovers,
|
||||
required this.extensions,
|
||||
});
|
||||
|
||||
bool get hasSettings => settings != null && settings!.isNotEmpty;
|
||||
@@ -49,13 +53,21 @@ class BackupBundle {
|
||||
int get playlistCount => _collectionListCount('playlists');
|
||||
int get favoriteArtistCount => _collectionListCount('favoriteArtists');
|
||||
|
||||
int get extensionCount {
|
||||
final items = extensions['items'];
|
||||
return items is List ? items.length : 0;
|
||||
}
|
||||
|
||||
bool get hasExtensions => extensionCount > 0;
|
||||
|
||||
bool get isEmpty =>
|
||||
!hasSettings &&
|
||||
historyCount == 0 &&
|
||||
likedCount == 0 &&
|
||||
wishlistCount == 0 &&
|
||||
playlistCount == 0 &&
|
||||
favoriteArtistCount == 0;
|
||||
favoriteArtistCount == 0 &&
|
||||
extensionCount == 0;
|
||||
}
|
||||
|
||||
/// Builds and parses SpotiFLAC backup files (a single JSON document containing
|
||||
@@ -73,6 +85,7 @@ class BackupService {
|
||||
required List<Map<String, dynamic>> history,
|
||||
required Map<String, dynamic> collections,
|
||||
required Map<String, dynamic> playlistCovers,
|
||||
required Map<String, dynamic> extensions,
|
||||
}) {
|
||||
return {
|
||||
'magic': magic,
|
||||
@@ -85,6 +98,7 @@ class BackupService {
|
||||
'history': history,
|
||||
'collections': collections,
|
||||
'playlist_covers': playlistCovers,
|
||||
'extensions': extensions,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -165,6 +179,11 @@ class BackupService {
|
||||
? Map<String, dynamic>.from(coversRaw)
|
||||
: <String, dynamic>{};
|
||||
|
||||
final extensionsRaw = data['extensions'];
|
||||
final extensions = extensionsRaw is Map
|
||||
? Map<String, dynamic>.from(extensionsRaw)
|
||||
: <String, dynamic>{};
|
||||
|
||||
return BackupBundle(
|
||||
formatVersion: (root['format_version'] as num?)?.toInt() ?? 1,
|
||||
appVersion: root['app_version'] as String? ?? '',
|
||||
@@ -173,6 +192,7 @@ class BackupService {
|
||||
history: history,
|
||||
collections: collections,
|
||||
playlistCovers: playlistCovers,
|
||||
extensions: extensions,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user