mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-05-14 21:08:04 +02:00
refactor: split large screen files into part files and DRY platform bridge
- Extract home_tab.dart helpers/widgets into home_tab_helpers.dart and home_tab_widgets.dart using Dart part files - Extract queue_tab.dart helpers/widgets into queue_tab_helpers.dart and queue_tab_widgets.dart using Dart part files - Extract track_metadata_edit_sheet.dart from track_metadata_screen.dart using Dart part file - Refactor _FileExistsListenableCache into a standalone class in queue_tab_helpers.dart - Fix artist_screen.dart: replace unreliable findAncestorStateOfType with GlobalKey for _FetchingProgressDialog progress updates - DRY platform_bridge.dart: extract common JSON decode patterns into reusable helper methods (_decodeRequiredMapResult, _decodeNullableMapResult, _decodeMapListResult, _decodeStringListResult)
This commit is contained in:
@@ -926,10 +926,12 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
final progressDialogKey = GlobalKey<_FetchingProgressDialogState>();
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (ctx) => _FetchingProgressDialog(
|
||||
key: progressDialogKey,
|
||||
totalAlbums: albums.length,
|
||||
onCancel: () {
|
||||
setState(() => _isFetchingDiscography = false);
|
||||
@@ -955,8 +957,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
fetchedCount++;
|
||||
|
||||
if (mounted) {
|
||||
_FetchingProgressDialog.updateProgress(
|
||||
context,
|
||||
progressDialogKey.currentState?.updateProgress(
|
||||
fetchedCount,
|
||||
albums.length,
|
||||
);
|
||||
@@ -2001,16 +2002,11 @@ class _FetchingProgressDialog extends StatefulWidget {
|
||||
final VoidCallback onCancel;
|
||||
|
||||
const _FetchingProgressDialog({
|
||||
super.key,
|
||||
required this.totalAlbums,
|
||||
required this.onCancel,
|
||||
});
|
||||
|
||||
static void updateProgress(BuildContext context, int current, int total) {
|
||||
final state = context
|
||||
.findAncestorStateOfType<_FetchingProgressDialogState>();
|
||||
state?._updateProgress(current, total);
|
||||
}
|
||||
|
||||
@override
|
||||
State<_FetchingProgressDialog> createState() =>
|
||||
_FetchingProgressDialogState();
|
||||
@@ -2026,7 +2022,7 @@ class _FetchingProgressDialogState extends State<_FetchingProgressDialog> {
|
||||
_total = widget.totalAlbums;
|
||||
}
|
||||
|
||||
void _updateProgress(int current, int total) {
|
||||
void updateProgress(int current, int total) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_current = current;
|
||||
|
||||
+3
-1924
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,188 @@
|
||||
part of 'home_tab.dart';
|
||||
|
||||
class _RecentAccessView {
|
||||
final List<RecentAccessItem> uniqueItems;
|
||||
final List<String> downloadIds;
|
||||
final Map<String, String> downloadFilePathByRecentKey;
|
||||
final bool hasHiddenDownloads;
|
||||
|
||||
const _RecentAccessView({
|
||||
required this.uniqueItems,
|
||||
required this.downloadIds,
|
||||
required this.downloadFilePathByRecentKey,
|
||||
required this.hasHiddenDownloads,
|
||||
});
|
||||
}
|
||||
|
||||
class _RecentAlbumAggregate {
|
||||
int count;
|
||||
DownloadHistoryItem mostRecent;
|
||||
|
||||
_RecentAlbumAggregate({required this.count, required this.mostRecent});
|
||||
}
|
||||
|
||||
class _CsvImportOptions {
|
||||
final bool confirmed;
|
||||
final bool skipDownloaded;
|
||||
|
||||
const _CsvImportOptions({
|
||||
required this.confirmed,
|
||||
required this.skipDownloaded,
|
||||
});
|
||||
}
|
||||
|
||||
class _SearchResultBuckets {
|
||||
final List<Track> realTracks;
|
||||
final List<int> realTrackIndexes;
|
||||
final List<Track> albumItems;
|
||||
final List<Track> playlistItems;
|
||||
final List<Track> artistItems;
|
||||
|
||||
const _SearchResultBuckets({
|
||||
required this.realTracks,
|
||||
required this.realTrackIndexes,
|
||||
required this.albumItems,
|
||||
required this.playlistItems,
|
||||
required this.artistItems,
|
||||
});
|
||||
}
|
||||
|
||||
enum _SearchSortOption {
|
||||
defaultOrder,
|
||||
titleAsc,
|
||||
titleDesc,
|
||||
artistAsc,
|
||||
artistDesc,
|
||||
durationAsc,
|
||||
durationDesc,
|
||||
dateAsc,
|
||||
dateDesc,
|
||||
}
|
||||
|
||||
const _homeHistoryPreviewLimit = 48;
|
||||
|
||||
class _HomeHistoryPreview {
|
||||
final List<DownloadHistoryItem> items;
|
||||
|
||||
const _HomeHistoryPreview(this.items);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is _HomeHistoryPreview && listEquals(items, other.items);
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll(items);
|
||||
}
|
||||
|
||||
final _homeHistoryPreviewProvider = Provider<List<DownloadHistoryItem>>((ref) {
|
||||
final preview = ref.watch(
|
||||
downloadHistoryProvider.select((s) {
|
||||
final items = s.items;
|
||||
if (items.length <= _homeHistoryPreviewLimit) {
|
||||
return _HomeHistoryPreview(items);
|
||||
}
|
||||
return _HomeHistoryPreview(
|
||||
items.take(_homeHistoryPreviewLimit).toList(growable: false),
|
||||
);
|
||||
}),
|
||||
);
|
||||
return preview.items;
|
||||
});
|
||||
|
||||
_RecentAccessView _buildRecentAccessViewData(
|
||||
List<RecentAccessItem> items,
|
||||
List<DownloadHistoryItem> historyItems,
|
||||
Set<String> hiddenIds,
|
||||
) {
|
||||
final albumGroups = <String, _RecentAlbumAggregate>{};
|
||||
for (final h in historyItems) {
|
||||
final artistForKey = (h.albumArtist != null && h.albumArtist!.isNotEmpty)
|
||||
? h.albumArtist!
|
||||
: h.artistName;
|
||||
final albumKey = '${h.albumName}|$artistForKey';
|
||||
final existing = albumGroups[albumKey];
|
||||
if (existing == null) {
|
||||
albumGroups[albumKey] = _RecentAlbumAggregate(count: 1, mostRecent: h);
|
||||
} else {
|
||||
existing.count++;
|
||||
if (h.downloadedAt.isAfter(existing.mostRecent.downloadedAt)) {
|
||||
existing.mostRecent = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final downloadIds = <String>[];
|
||||
final visibleDownloads = <RecentAccessItem>[];
|
||||
final downloadFilePathByRecentKey = <String, String>{};
|
||||
for (final aggregate in albumGroups.values) {
|
||||
final mostRecent = aggregate.mostRecent;
|
||||
final artistForKey =
|
||||
(mostRecent.albumArtist != null && mostRecent.albumArtist!.isNotEmpty)
|
||||
? mostRecent.albumArtist!
|
||||
: mostRecent.artistName;
|
||||
|
||||
final isSingleTrack = aggregate.count == 1;
|
||||
final recentId = isSingleTrack
|
||||
? (mostRecent.spotifyId ?? mostRecent.id)
|
||||
: '${mostRecent.albumName}|$artistForKey';
|
||||
final recent = RecentAccessItem(
|
||||
id: recentId,
|
||||
name: isSingleTrack ? mostRecent.trackName : mostRecent.albumName,
|
||||
subtitle: isSingleTrack ? mostRecent.artistName : artistForKey,
|
||||
imageUrl: mostRecent.coverUrl,
|
||||
type: isSingleTrack ? RecentAccessType.track : RecentAccessType.album,
|
||||
accessedAt: mostRecent.downloadedAt,
|
||||
providerId: 'download',
|
||||
);
|
||||
|
||||
downloadIds.add(recentId);
|
||||
downloadFilePathByRecentKey['${recent.type.name}:${recent.id}'] =
|
||||
mostRecent.filePath;
|
||||
if (!hiddenIds.contains(recentId)) {
|
||||
visibleDownloads.add(recent);
|
||||
}
|
||||
}
|
||||
|
||||
visibleDownloads.sort((a, b) => b.accessedAt.compareTo(a.accessedAt));
|
||||
if (visibleDownloads.length > 10) {
|
||||
visibleDownloads.removeRange(10, visibleDownloads.length);
|
||||
}
|
||||
|
||||
final allItems = <RecentAccessItem>[...items, ...visibleDownloads];
|
||||
allItems.sort((a, b) => b.accessedAt.compareTo(a.accessedAt));
|
||||
|
||||
final seen = <String>{};
|
||||
final uniqueItems = <RecentAccessItem>[];
|
||||
for (final item in allItems) {
|
||||
final key = '${item.type.name}:${item.id}';
|
||||
if (seen.add(key)) {
|
||||
uniqueItems.add(item);
|
||||
if (uniqueItems.length >= 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _RecentAccessView(
|
||||
uniqueItems: uniqueItems,
|
||||
downloadIds: downloadIds,
|
||||
downloadFilePathByRecentKey: downloadFilePathByRecentKey,
|
||||
hasHiddenDownloads: hiddenIds.isNotEmpty,
|
||||
);
|
||||
}
|
||||
|
||||
final recentAccessViewProvider = Provider<_RecentAccessView>((ref) {
|
||||
final historyItems = ref.watch(_homeHistoryPreviewProvider);
|
||||
final recentAccessItems = ref.watch(
|
||||
recentAccessProvider.select((s) => s.items),
|
||||
);
|
||||
final hiddenDownloadIds = ref.watch(
|
||||
recentAccessProvider.select((s) => s.hiddenDownloadIds),
|
||||
);
|
||||
return _buildRecentAccessViewData(
|
||||
recentAccessItems,
|
||||
historyItems,
|
||||
hiddenDownloadIds,
|
||||
);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
+7
-1293
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,200 @@
|
||||
part of 'queue_tab.dart';
|
||||
|
||||
class _QueueItemSliverRow extends ConsumerWidget {
|
||||
final String itemId;
|
||||
final ColorScheme colorScheme;
|
||||
final Widget Function(BuildContext, DownloadItem, ColorScheme) itemBuilder;
|
||||
|
||||
const _QueueItemSliverRow({
|
||||
super.key,
|
||||
required this.itemId,
|
||||
required this.colorScheme,
|
||||
required this.itemBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final item = ref.watch(
|
||||
downloadQueueLookupProvider.select((lookup) => lookup.byItemId[itemId]),
|
||||
);
|
||||
if (item == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return RepaintBoundary(child: itemBuilder(context, item, colorScheme));
|
||||
}
|
||||
}
|
||||
|
||||
enum _CollectionEntryType { wishlist, loved, playlist }
|
||||
|
||||
class _CollectionEntry {
|
||||
final _CollectionEntryType type;
|
||||
final int playlistIndex;
|
||||
|
||||
const _CollectionEntry._(this.type, [this.playlistIndex = -1]);
|
||||
|
||||
static const wishlist = _CollectionEntry._(_CollectionEntryType.wishlist);
|
||||
static const loved = _CollectionEntry._(_CollectionEntryType.loved);
|
||||
static _CollectionEntry playlist(int index) =>
|
||||
_CollectionEntry._(_CollectionEntryType.playlist, index);
|
||||
}
|
||||
|
||||
class _FilterChip extends StatelessWidget {
|
||||
final String label;
|
||||
final int count;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _FilterChip({
|
||||
required this.label,
|
||||
required this.count,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return FilterChip(
|
||||
label: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(label),
|
||||
const SizedBox(width: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? colorScheme.primary.withValues(alpha: 0.2)
|
||||
: colorScheme.outline.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
count.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: isSelected
|
||||
? colorScheme.onPrimaryContainer
|
||||
: colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
selected: isSelected,
|
||||
onSelected: (_) => onTap(),
|
||||
showCheckmark: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SelectionActionButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final VoidCallback? onPressed;
|
||||
final ColorScheme colorScheme;
|
||||
|
||||
const _SelectionActionButton({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
required this.colorScheme,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDisabled = onPressed == null;
|
||||
return Material(
|
||||
color: isDisabled
|
||||
? colorScheme.surfaceContainerHighest.withValues(alpha: 0.5)
|
||||
: colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 18,
|
||||
color: isDisabled
|
||||
? colorScheme.onSurfaceVariant.withValues(alpha: 0.5)
|
||||
: colorScheme.onSecondaryContainer,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDisabled
|
||||
? colorScheme.onSurfaceVariant.withValues(alpha: 0.5)
|
||||
: colorScheme.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AnimatedOverlayBottomBar extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const _AnimatedOverlayBottomBar({required this.child});
|
||||
|
||||
@override
|
||||
State<_AnimatedOverlayBottomBar> createState() =>
|
||||
_AnimatedOverlayBottomBarState();
|
||||
}
|
||||
|
||||
class _AnimatedOverlayBottomBarState extends State<_AnimatedOverlayBottomBar>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<Offset> _slideAnimation;
|
||||
late final Animation<double> _fadeAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 240),
|
||||
);
|
||||
final curve = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeOutCubic,
|
||||
);
|
||||
_slideAnimation = Tween<Offset>(
|
||||
begin: const Offset(0, 0.08),
|
||||
end: Offset.zero,
|
||||
).animate(curve);
|
||||
_fadeAnimation = Tween<double>(begin: 0, end: 1).animate(curve);
|
||||
_controller.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(position: _slideAnimation, child: widget.child),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ class PlatformBridge {
|
||||
'spotify_id': spotifyId,
|
||||
'isrc': isrc,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'checkAvailability');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> _invokeDownloadMethod(
|
||||
@@ -38,7 +38,7 @@ class PlatformBridge {
|
||||
) async {
|
||||
final request = jsonEncode(payload.toJson());
|
||||
final result = await _channel.invokeMethod(method, request);
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, method);
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> downloadByStrategy({
|
||||
@@ -133,7 +133,7 @@ class PlatformBridge {
|
||||
'output_dir': outputDir,
|
||||
'isrc': isrc,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'checkDuplicate');
|
||||
}
|
||||
|
||||
static Future<String> buildFilename(
|
||||
@@ -156,8 +156,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<Map<String, dynamic>?> pickSafTree() async {
|
||||
final result = await _channel.invokeMethod('pickSafTree');
|
||||
if (result == null) return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'pickSafTree');
|
||||
}
|
||||
|
||||
static Future<bool> safExists(String uri) async {
|
||||
@@ -172,7 +171,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<Map<String, dynamic>> safStat(String uri) async {
|
||||
final result = await _channel.invokeMethod('safStat', {'uri': uri});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'safStat');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> resolveSafFile({
|
||||
@@ -185,7 +184,7 @@ class PlatformBridge {
|
||||
'relative_dir': relativeDir,
|
||||
'file_name': fileName,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'resolveSafFile');
|
||||
}
|
||||
|
||||
static Future<String?> copyContentUriToTemp(String uri) async {
|
||||
@@ -259,7 +258,7 @@ class PlatformBridge {
|
||||
'artist_name': artistName,
|
||||
'duration_ms': durationMs,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'fetchLyrics');
|
||||
}
|
||||
|
||||
static Future<String> getLyricsLRC(
|
||||
@@ -293,7 +292,7 @@ class PlatformBridge {
|
||||
'file_path': filePath ?? '',
|
||||
'duration_ms': durationMs,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'getLyricsLRCWithSource');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> embedLyricsToFile(
|
||||
@@ -304,7 +303,7 @@ class PlatformBridge {
|
||||
'file_path': filePath,
|
||||
'lyrics': lyrics,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'embedLyricsToFile');
|
||||
}
|
||||
|
||||
static Future<void> cleanupConnections() async {
|
||||
@@ -321,7 +320,7 @@ class PlatformBridge {
|
||||
'output_path': outputPath,
|
||||
'max_quality': maxQuality,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'downloadCoverToFile');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> extractCoverToFile(
|
||||
@@ -332,7 +331,7 @@ class PlatformBridge {
|
||||
'audio_path': audioPath,
|
||||
'output_path': outputPath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'extractCoverToFile');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> fetchAndSaveLyrics({
|
||||
@@ -351,7 +350,7 @@ class PlatformBridge {
|
||||
'output_path': outputPath,
|
||||
'audio_file_path': audioFilePath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'fetchAndSaveLyrics');
|
||||
}
|
||||
|
||||
/// Providers not in the list are disabled.
|
||||
@@ -364,15 +363,13 @@ class PlatformBridge {
|
||||
|
||||
static Future<List<String>> getLyricsProviders() async {
|
||||
final result = await _channel.invokeMethod('getLyricsProviders');
|
||||
final List<dynamic> decoded = jsonDecode(result as String) as List<dynamic>;
|
||||
return decoded.cast<String>();
|
||||
return _decodeStringListResult(result, 'getLyricsProviders');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>>
|
||||
getAvailableLyricsProviders() async {
|
||||
final result = await _channel.invokeMethod('getAvailableLyricsProviders');
|
||||
final List<dynamic> decoded = jsonDecode(result as String) as List<dynamic>;
|
||||
return decoded.cast<Map<String, dynamic>>();
|
||||
return _decodeMapListResult(result, 'getAvailableLyricsProviders');
|
||||
}
|
||||
|
||||
/// Sets advanced lyrics fetch options used by provider-specific integrations.
|
||||
@@ -387,7 +384,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<Map<String, dynamic>> getLyricsFetchOptions() async {
|
||||
final result = await _channel.invokeMethod('getLyricsFetchOptions');
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'getLyricsFetchOptions');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> reEnrichFile(
|
||||
@@ -397,14 +394,14 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('reEnrichFile', {
|
||||
'request_json': requestJSON,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'reEnrichFile');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> readFileMetadata(String filePath) async {
|
||||
final result = await _channel.invokeMethod('readFileMetadata', {
|
||||
'file_path': filePath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'readFileMetadata');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> editFileMetadata(
|
||||
@@ -416,7 +413,7 @@ class PlatformBridge {
|
||||
'file_path': filePath,
|
||||
'metadata_json': metadataJSON,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'editFileMetadata');
|
||||
}
|
||||
|
||||
/// Rewrites ARTIST/ALBUMARTIST Vorbis comments as multiple split entries
|
||||
@@ -431,7 +428,7 @@ class PlatformBridge {
|
||||
'artist': artist,
|
||||
'album_artist': albumArtist,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'rewriteSplitArtistTags');
|
||||
}
|
||||
|
||||
static Future<bool> writeTempToSaf(String tempPath, String safUri) async {
|
||||
@@ -439,7 +436,7 @@ class PlatformBridge {
|
||||
'temp_path': tempPath,
|
||||
'saf_uri': safUri,
|
||||
});
|
||||
final map = jsonDecode(result as String) as Map<String, dynamic>;
|
||||
final map = _decodeRequiredMapResult(result, 'writeTempToSaf');
|
||||
return map['success'] == true;
|
||||
}
|
||||
|
||||
@@ -510,7 +507,7 @@ class PlatformBridge {
|
||||
'artist_limit': artistLimit,
|
||||
'filter': filter ?? '',
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'searchProviderAll');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getDeezerRelatedArtists(
|
||||
@@ -521,14 +518,14 @@ class PlatformBridge {
|
||||
'artist_id': artistId,
|
||||
'limit': limit,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'getDeezerRelatedArtists');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> parseProviderUrl(String url) async {
|
||||
final result = await _channel.invokeMethod('parseProviderUrl', {
|
||||
'url': url,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'parseProviderUrl');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getProviderMetadata(
|
||||
@@ -546,7 +543,7 @@ class PlatformBridge {
|
||||
'getProviderMetadata returned null for $providerId:$resourceType:$resourceId',
|
||||
);
|
||||
}
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'getProviderMetadata');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> searchDeezerByISRC(
|
||||
@@ -557,7 +554,7 @@ class PlatformBridge {
|
||||
'isrc': isrc,
|
||||
'item_id': itemId ?? '',
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'searchDeezerByISRC');
|
||||
}
|
||||
|
||||
static Future<Map<String, String>?> getDeezerExtendedMetadata(
|
||||
@@ -568,7 +565,10 @@ class PlatformBridge {
|
||||
'track_id': trackId,
|
||||
});
|
||||
if (result == null) return null;
|
||||
final data = jsonDecode(result as String) as Map<String, dynamic>;
|
||||
final data = _decodeRequiredMapResult(
|
||||
result,
|
||||
'getDeezerExtendedMetadata',
|
||||
);
|
||||
return {
|
||||
'genre': data['genre'] as String? ?? '',
|
||||
'label': data['label'] as String? ?? '',
|
||||
@@ -588,20 +588,19 @@ class PlatformBridge {
|
||||
'resource_type': resourceType,
|
||||
'spotify_id': spotifyId,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'convertSpotifyToDeezer');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getGoLogs() async {
|
||||
final result = await _channel.invokeMethod('getLogs');
|
||||
final logs = jsonDecode(result as String) as List<dynamic>;
|
||||
return logs.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getGoLogs');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getGoLogsSince(int index) async {
|
||||
final result = await _channel.invokeMethod('getLogsSince', {
|
||||
'index': index,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'getGoLogsSince');
|
||||
}
|
||||
|
||||
static Future<void> clearGoLogs() async {
|
||||
@@ -635,7 +634,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('loadExtensionsFromDir', {
|
||||
'dir_path': dirPath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'loadExtensionsFromDir');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> loadExtensionFromPath(
|
||||
@@ -645,7 +644,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('loadExtensionFromPath', {
|
||||
'file_path': filePath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'loadExtensionFromPath');
|
||||
}
|
||||
|
||||
static Future<void> unloadExtension(String extensionId) async {
|
||||
@@ -667,7 +666,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('upgradeExtension', {
|
||||
'file_path': filePath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'upgradeExtension');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> checkExtensionUpgrade(
|
||||
@@ -677,13 +676,12 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('checkExtensionUpgrade', {
|
||||
'file_path': filePath,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'checkExtensionUpgrade');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getInstalledExtensions() async {
|
||||
final result = await _channel.invokeMethod('getInstalledExtensions');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getInstalledExtensions');
|
||||
}
|
||||
|
||||
static Future<void> setExtensionEnabled(
|
||||
@@ -706,8 +704,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<List<String>> getProviderPriority() async {
|
||||
final result = await _channel.invokeMethod('getProviderPriority');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as String).toList();
|
||||
return _decodeStringListResult(result, 'getProviderPriority');
|
||||
}
|
||||
|
||||
static Future<void> setDownloadFallbackExtensionIds(
|
||||
@@ -730,8 +727,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<List<String>> getMetadataProviderPriority() async {
|
||||
final result = await _channel.invokeMethod('getMetadataProviderPriority');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as String).toList();
|
||||
return _decodeStringListResult(result, 'getMetadataProviderPriority');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getExtensionSettings(
|
||||
@@ -740,7 +736,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('getExtensionSettings', {
|
||||
'extension_id': extensionId,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'getExtensionSettings');
|
||||
}
|
||||
|
||||
static Future<void> setExtensionSettings(
|
||||
@@ -766,7 +762,7 @@ class PlatformBridge {
|
||||
if (result == null || (result as String).isEmpty) {
|
||||
return {'success': true};
|
||||
}
|
||||
return jsonDecode(result) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'invokeExtensionAction');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> searchTracksWithExtensions(
|
||||
@@ -778,8 +774,7 @@ class PlatformBridge {
|
||||
'query': query,
|
||||
'limit': limit,
|
||||
});
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'searchTracksWithExtensions');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> searchTracksWithMetadataProviders(
|
||||
@@ -794,8 +789,7 @@ class PlatformBridge {
|
||||
'searchTracksWithMetadataProviders',
|
||||
{'query': query, 'limit': limit, 'include_extensions': includeExtensions},
|
||||
);
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'searchTracksWithMetadataProviders');
|
||||
}
|
||||
|
||||
static Future<void> cleanupExtensions() async {
|
||||
@@ -809,8 +803,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('getExtensionPendingAuth', {
|
||||
'extension_id': extensionId,
|
||||
});
|
||||
if (result == null) return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'getExtensionPendingAuth');
|
||||
}
|
||||
|
||||
static Future<void> setExtensionAuthCode(
|
||||
@@ -854,8 +847,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getAllPendingAuthRequests() async {
|
||||
final result = await _channel.invokeMethod('getAllPendingAuthRequests');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getAllPendingAuthRequests');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>?> getPendingFFmpegCommand(
|
||||
@@ -864,8 +856,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('getPendingFFmpegCommand', {
|
||||
'command_id': commandId,
|
||||
});
|
||||
if (result == null) return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'getPendingFFmpegCommand');
|
||||
}
|
||||
|
||||
static Future<void> setFFmpegCommandResult(
|
||||
@@ -885,8 +876,7 @@ class PlatformBridge {
|
||||
static Future<List<Map<String, dynamic>>>
|
||||
getAllPendingFFmpegCommands() async {
|
||||
final result = await _channel.invokeMethod('getAllPendingFFmpegCommands');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'setFFmpegCommandResult');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> customSearchWithExtension(
|
||||
@@ -899,20 +889,17 @@ class PlatformBridge {
|
||||
'query': query,
|
||||
'options': options != null ? jsonEncode(options) : '',
|
||||
});
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'customSearchWithExtension');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getSearchProviders() async {
|
||||
final result = await _channel.invokeMethod('getSearchProviders');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getSearchProviders');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getBuiltInProviders() async {
|
||||
final result = await _channel.invokeMethod('getBuiltInProviders');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getBuiltInProviders');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>?> handleURLWithExtension(
|
||||
@@ -922,8 +909,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('handleURLWithExtension', {
|
||||
'url': url,
|
||||
});
|
||||
if (result == null || result == '') return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'handleURLWithExtension');
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
@@ -937,8 +923,7 @@ class PlatformBridge {
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getURLHandlers() async {
|
||||
final result = await _channel.invokeMethod('getURLHandlers');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getURLHandlers');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>?> getExtensionHomeFeed(
|
||||
@@ -948,8 +933,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('getExtensionHomeFeed', {
|
||||
'extension_id': extensionId,
|
||||
});
|
||||
if (result == null || result == '') return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'getExtensionHomeFeed');
|
||||
} catch (e) {
|
||||
_log.e('getExtensionHomeFeed failed: $e');
|
||||
return null;
|
||||
@@ -964,8 +948,7 @@ class PlatformBridge {
|
||||
'getExtensionBrowseCategories',
|
||||
{'extension_id': extensionId},
|
||||
);
|
||||
if (result == null || result == '') return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'getExtensionBrowseCategories');
|
||||
} catch (e) {
|
||||
_log.e('getExtensionBrowseCategories failed: $e');
|
||||
return null;
|
||||
@@ -986,8 +969,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('scanLibraryFolder', {
|
||||
'folder_path': folderPath,
|
||||
});
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'scanLibraryFolder');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> scanLibraryFolderIncremental(
|
||||
@@ -1001,7 +983,7 @@ class PlatformBridge {
|
||||
'folder_path': folderPath,
|
||||
'existing_files': jsonEncode(existingFiles),
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'scanLibraryFolderIncremental');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> scanLibraryFolderIncrementalFromSnapshot(
|
||||
@@ -1012,7 +994,10 @@ class PlatformBridge {
|
||||
'scanLibraryFolderIncrementalFromSnapshot',
|
||||
{'folder_path': folderPath, 'snapshot_path': snapshotPath},
|
||||
);
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(
|
||||
result,
|
||||
'scanLibraryFolderIncrementalFromSnapshot',
|
||||
);
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> scanSafTree(String treeUri) async {
|
||||
@@ -1020,8 +1005,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('scanSafTree', {
|
||||
'tree_uri': treeUri,
|
||||
});
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'scanSafTree');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> scanSafTreeIncremental(
|
||||
@@ -1035,7 +1019,7 @@ class PlatformBridge {
|
||||
'tree_uri': treeUri,
|
||||
'existing_files': jsonEncode(existingFiles),
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'scanSafTreeIncremental');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> scanSafTreeIncrementalFromSnapshot(
|
||||
@@ -1046,14 +1030,17 @@ class PlatformBridge {
|
||||
'scanSafTreeIncrementalFromSnapshot',
|
||||
{'tree_uri': treeUri, 'snapshot_path': snapshotPath},
|
||||
);
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(
|
||||
result,
|
||||
'scanSafTreeIncrementalFromSnapshot',
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Map<String, int>> getSafFileModTimes(List<String> uris) async {
|
||||
final result = await _channel.invokeMethod('getSafFileModTimes', {
|
||||
'uris': jsonEncode(uris),
|
||||
});
|
||||
final map = jsonDecode(result as String) as Map<String, dynamic>;
|
||||
final map = _decodeRequiredMapResult(result, 'getSafFileModTimes');
|
||||
return map.map((key, value) => MapEntry(key, (value as num).toInt()));
|
||||
}
|
||||
|
||||
@@ -1072,6 +1059,73 @@ class PlatformBridge {
|
||||
await _channel.invokeMethod('cancelLibraryScan');
|
||||
}
|
||||
|
||||
static Object? _decodeJsonResult(dynamic result) {
|
||||
if (result is String) {
|
||||
if (result.isEmpty) return null;
|
||||
return jsonDecode(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Map<String, dynamic> _decodeRequiredMapResult(
|
||||
dynamic result,
|
||||
String method,
|
||||
) {
|
||||
final decoded = _decodeJsonResult(result);
|
||||
if (decoded is Map) {
|
||||
return decoded.cast<String, dynamic>();
|
||||
}
|
||||
throw FormatException(
|
||||
'Expected map result from $method, got ${decoded.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
static Map<String, dynamic>? _decodeNullableMapResult(
|
||||
dynamic result,
|
||||
String method,
|
||||
) {
|
||||
final decoded = _decodeJsonResult(result);
|
||||
if (decoded == null) return null;
|
||||
if (decoded is Map) {
|
||||
return decoded.cast<String, dynamic>();
|
||||
}
|
||||
throw FormatException(
|
||||
'Expected nullable map result from $method, got ${decoded.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
static List<dynamic> _decodeRequiredListResult(
|
||||
dynamic result,
|
||||
String method,
|
||||
) {
|
||||
final decoded = _decodeJsonResult(result);
|
||||
if (decoded is List) return decoded;
|
||||
throw FormatException(
|
||||
'Expected list result from $method, got ${decoded.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
static List<Map<String, dynamic>> _decodeMapListResult(
|
||||
dynamic result,
|
||||
String method,
|
||||
) {
|
||||
return _decodeRequiredListResult(result, method).map((entry) {
|
||||
if (entry is Map) return entry.cast<String, dynamic>();
|
||||
throw FormatException(
|
||||
'Expected map entry from $method, got ${entry.runtimeType}',
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
static List<String> _decodeStringListResult(dynamic result, String method) {
|
||||
return _decodeRequiredListResult(result, method).map((entry) {
|
||||
if (entry is String) return entry;
|
||||
throw FormatException(
|
||||
'Expected string entry from $method, got ${entry.runtimeType}',
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
static Map<String, dynamic> _decodeMapResult(dynamic result) {
|
||||
if (result is Map) {
|
||||
return result.cast<String, dynamic>();
|
||||
@@ -1134,8 +1188,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('readAudioMetadata', {
|
||||
'file_path': filePath,
|
||||
});
|
||||
if (result == null || result == '') return null;
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeNullableMapResult(result, 'readAudioMetadata');
|
||||
} catch (e) {
|
||||
_log.w('Failed to read audio metadata: $e');
|
||||
return null;
|
||||
@@ -1150,7 +1203,7 @@ class PlatformBridge {
|
||||
'file_path': filePath,
|
||||
'metadata': metadata != null ? jsonEncode(metadata) : '',
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'runPostProcessing');
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> runPostProcessingV2(
|
||||
@@ -1167,13 +1220,12 @@ class PlatformBridge {
|
||||
'input': jsonEncode(input),
|
||||
'metadata': metadata != null ? jsonEncode(metadata) : '',
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'runPostProcessingV2');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> getPostProcessingProviders() async {
|
||||
final result = await _channel.invokeMethod('getPostProcessingProviders');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getPostProcessingProviders');
|
||||
}
|
||||
|
||||
static Future<void> initExtensionStore(String cacheDir) async {
|
||||
@@ -1206,8 +1258,7 @@ class PlatformBridge {
|
||||
final result = await _channel.invokeMethod('getStoreExtensions', {
|
||||
'force_refresh': forceRefresh,
|
||||
});
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'getStoreExtensions');
|
||||
}
|
||||
|
||||
static Future<List<Map<String, dynamic>>> searchStoreExtensions(
|
||||
@@ -1219,14 +1270,12 @@ class PlatformBridge {
|
||||
'query': query,
|
||||
'category': category ?? '',
|
||||
});
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.map((e) => e as Map<String, dynamic>).toList();
|
||||
return _decodeMapListResult(result, 'searchStoreExtensions');
|
||||
}
|
||||
|
||||
static Future<List<String>> getStoreCategories() async {
|
||||
final result = await _channel.invokeMethod('getStoreCategories');
|
||||
final list = jsonDecode(result as String) as List<dynamic>;
|
||||
return list.cast<String>();
|
||||
return _decodeStringListResult(result, 'getStoreCategories');
|
||||
}
|
||||
|
||||
static Future<String> downloadStoreExtension(
|
||||
@@ -1255,6 +1304,6 @@ class PlatformBridge {
|
||||
'cue_path': cuePath,
|
||||
'audio_dir': audioDir,
|
||||
});
|
||||
return jsonDecode(result as String) as Map<String, dynamic>;
|
||||
return _decodeRequiredMapResult(result, 'parseCueSheet');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user