mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-12 02:37:18 +02:00
feat: add built-in Tidal/Qobuz search with recommended service picker
- Add SearchAll() for Tidal and Qobuz in Go backend (tracks, artists, albums) - Add searchTidalAll/searchQobuzAll platform routing for Android and iOS - Add Tidal/Qobuz options to search provider dropdown in home tab - Show (Recommended) label and auto-select service in download picker
This commit is contained in:
1 parent
98fdc0ed7c
commit
4f365ca7fe
20 files changed
+720
-97
No files matched your search
@@ -1191,7 +1191,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
Future<void> _pickDirectory(BuildContext context, WidgetRef ref) async {
|
||||
if (Platform.isIOS) {
|
||||
_showIOSDirectoryOptions(context, ref);
|
||||
} else {
|
||||
} else if (Platform.isAndroid) {
|
||||
_showAndroidDirectoryOptions(context, ref);
|
||||
}
|
||||
}
|
||||
@@ -1626,9 +1626,9 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
context.l10n.downloadLossy320Format,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
|
||||
@@ -73,11 +73,13 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
} else if (Platform.isIOS) {
|
||||
// iOS doesn't need explicit storage permission for app documents
|
||||
setState(() => _hasStoragePermission = true);
|
||||
} else {
|
||||
setState(() => _hasStoragePermission = true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _requestStoragePermission() async {
|
||||
if (Platform.isIOS) return true;
|
||||
if (!Platform.isAndroid) return true;
|
||||
// SAF on Android 10+ doesn't need MANAGE_EXTERNAL_STORAGE
|
||||
if (_androidSdkVersion >= 29) return true;
|
||||
|
||||
@@ -135,8 +137,9 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
if (Platform.isIOS) {
|
||||
// On iOS, create a security-scoped bookmark so we can access
|
||||
// this folder across app restarts and from the Go backend.
|
||||
final bookmark =
|
||||
await PlatformBridge.createIosBookmarkFromPath(result);
|
||||
final bookmark = await PlatformBridge.createIosBookmarkFromPath(
|
||||
result,
|
||||
);
|
||||
if (bookmark != null && bookmark.isNotEmpty) {
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
@@ -182,11 +185,13 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref.read(localLibraryProvider.notifier).startScan(
|
||||
libraryPath,
|
||||
forceFullScan: forceFullScan,
|
||||
iosBookmark: iosBookmark.isNotEmpty ? iosBookmark : null,
|
||||
);
|
||||
await ref
|
||||
.read(localLibraryProvider.notifier)
|
||||
.startScan(
|
||||
libraryPath,
|
||||
forceFullScan: forceFullScan,
|
||||
iosBookmark: iosBookmark.isNotEmpty ? iosBookmark : null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _cancelScan() async {
|
||||
@@ -272,10 +277,9 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
context.l10n.libraryAutoScan,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
@@ -293,7 +297,9 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
selected: current == 'off',
|
||||
colorScheme: colorScheme,
|
||||
onTap: () {
|
||||
ref.read(settingsProvider.notifier).setLocalLibraryAutoScan('off');
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setLocalLibraryAutoScan('off');
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
@@ -303,7 +309,9 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
selected: current == 'on_open',
|
||||
colorScheme: colorScheme,
|
||||
onTap: () {
|
||||
ref.read(settingsProvider.notifier).setLocalLibraryAutoScan('on_open');
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setLocalLibraryAutoScan('on_open');
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
@@ -313,7 +321,9 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
selected: current == 'daily',
|
||||
colorScheme: colorScheme,
|
||||
onTap: () {
|
||||
ref.read(settingsProvider.notifier).setLocalLibraryAutoScan('daily');
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setLocalLibraryAutoScan('daily');
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
@@ -323,7 +333,9 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
selected: current == 'weekly',
|
||||
colorScheme: colorScheme,
|
||||
onTap: () {
|
||||
ref.read(settingsProvider.notifier).setLocalLibraryAutoScan('weekly');
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setLocalLibraryAutoScan('weekly');
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
@@ -443,9 +455,15 @@ class _LibrarySettingsPageState extends ConsumerState<LibrarySettingsPage> {
|
||||
child: SettingsItem(
|
||||
icon: Icons.autorenew_rounded,
|
||||
title: context.l10n.libraryAutoScan,
|
||||
subtitle: _getAutoScanLabel(context, settings.localLibraryAutoScan),
|
||||
subtitle: _getAutoScanLabel(
|
||||
context,
|
||||
settings.localLibraryAutoScan,
|
||||
),
|
||||
onTap: settings.localLibraryEnabled
|
||||
? () => _showAutoScanPicker(context, settings.localLibraryAutoScan)
|
||||
? () => _showAutoScanPicker(
|
||||
context,
|
||||
settings.localLibraryAutoScan,
|
||||
)
|
||||
: null,
|
||||
showDivider: false,
|
||||
),
|
||||
@@ -950,9 +968,7 @@ class _AutoScanOption extends StatelessWidget {
|
||||
return ListTile(
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
trailing: selected
|
||||
? Icon(Icons.check, color: colorScheme.primary)
|
||||
: null,
|
||||
trailing: selected ? Icon(Icons.check, color: colorScheme.primary) : null,
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user