Compare commits
3 Commits
463cb9870f
...
222d50d80d
| Author | SHA1 | Date | |
|---|---|---|---|
| 222d50d80d | |||
| 499bca5b1c | |||
| 252e4619f7 |
@@ -1,59 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Groovy Box</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Groovy Box</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.music</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>To provide information for RhythmBox to normalize the output audio</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Groovy Box</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Groovy Box</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.music</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>To provide information for RhythmBox to normalize the output audio</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -82,34 +82,38 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
children: [
|
||||
Obx(
|
||||
() => LimitedBox(
|
||||
maxHeight: maxAlbumSize,
|
||||
maxWidth: maxAlbumSize,
|
||||
child: Hero(
|
||||
tag: const Key('current-active-track-album-art'),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: ClipRRect(
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(16)),
|
||||
child: _albumArt != null
|
||||
? AutoCacheImage(
|
||||
_albumArt!,
|
||||
width: albumSize,
|
||||
height: albumSize,
|
||||
)
|
||||
: Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
width: 64,
|
||||
height: 64,
|
||||
child: const Center(
|
||||
child: Icon(Icons.image),
|
||||
() => Center(
|
||||
child: LimitedBox(
|
||||
maxHeight: maxAlbumSize,
|
||||
maxWidth: maxAlbumSize,
|
||||
child: Hero(
|
||||
tag: const Key('current-active-track-album-art'),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(16),
|
||||
),
|
||||
child: _albumArt != null
|
||||
? AutoCacheImage(
|
||||
_albumArt!,
|
||||
width: albumSize,
|
||||
height: albumSize,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
width: 64,
|
||||
height: 64,
|
||||
child: const Center(
|
||||
child: Icon(Icons.image),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
).marginSymmetric(horizontal: 24),
|
||||
),
|
||||
).marginSymmetric(horizontal: 24),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -309,87 +313,89 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||
],
|
||||
),
|
||||
const Gap(20),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.queue_music),
|
||||
label: const Text(
|
||||
'Queue',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const PlayerQueuePopup(),
|
||||
).then((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!isLargeScreen) const Gap(4),
|
||||
if (!isLargeScreen)
|
||||
Center(
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.lyrics),
|
||||
icon: const Icon(Icons.queue_music),
|
||||
label: const Text(
|
||||
'Lyrics',
|
||||
'Queue',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
onPressed: () {
|
||||
GoRouter.of(context)
|
||||
.pushNamed('playerLyrics');
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const PlayerQueuePopup(),
|
||||
).then((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const Gap(4),
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.merge),
|
||||
label: const Text(
|
||||
'Sources',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
if (!isLargeScreen) const Gap(4),
|
||||
if (!isLargeScreen)
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.lyrics),
|
||||
label: const Text(
|
||||
'Lyrics',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
onPressed: () {
|
||||
GoRouter.of(context)
|
||||
.pushNamed('playerLyrics');
|
||||
},
|
||||
),
|
||||
const Gap(4),
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.merge),
|
||||
label: const Text(
|
||||
'Sources',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const SiblingTracksPopup(),
|
||||
).then((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const SiblingTracksPopup(),
|
||||
).then((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const Gap(4),
|
||||
TextButton.icon(
|
||||
label: const Text('Info'),
|
||||
icon: const Icon(Icons.info),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const SourceDetailsPopup(),
|
||||
).then((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
const Gap(4),
|
||||
TextButton.icon(
|
||||
label: const Text('Info'),
|
||||
icon: const Icon(Icons.info),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const SourceDetailsPopup(),
|
||||
).then((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -109,8 +109,9 @@ abstract class SourcedTrack extends Track {
|
||||
_ => await YoutubeSourcedTrack.fetchFromTrack(track: track),
|
||||
};
|
||||
} on TrackNotFoundError catch (err) {
|
||||
Get.find<ErrorNotifier>()
|
||||
.showError('${err.toString()} via ${preferences.audioSource.label}');
|
||||
Get.find<ErrorNotifier>().showError(
|
||||
'${err.toString()} via ${preferences.audioSource.label}, querying in fallback sources...',
|
||||
);
|
||||
return switch (preferences.audioSource) {
|
||||
AudioSource.piped ||
|
||||
AudioSource.youtube =>
|
||||
|
||||
@@ -93,6 +93,12 @@ class NeteaseSourcedTrack extends SourcedTrack {
|
||||
throw TrackNotFoundError(track);
|
||||
}
|
||||
|
||||
final client = getClient();
|
||||
final checkResp = await client.get(
|
||||
'/check/music?id=${siblings.first.info.id}&realIP=${await lookupRealIp()}',
|
||||
);
|
||||
if (checkResp.body['success'] != true) throw TrackNotFoundError(track);
|
||||
|
||||
await db.database.into(db.database.sourceMatchTable).insert(
|
||||
SourceMatchTableCompanion.insert(
|
||||
trackId: track.id!,
|
||||
@@ -111,8 +117,14 @@ class NeteaseSourcedTrack extends SourcedTrack {
|
||||
|
||||
final client = getClient();
|
||||
final resp = await client.get('/song/detail?ids=${cachedSource.sourceId}');
|
||||
print(resp.body);
|
||||
final item = resp.body['songs'][0];
|
||||
final item = (resp.body['songs'] as List<dynamic>).firstOrNull;
|
||||
|
||||
if (item == null) throw TrackNotFoundError(track);
|
||||
|
||||
final checkResp = await client.get(
|
||||
'/check/music?id=${item['id']}&realIP=${await lookupRealIp()}',
|
||||
);
|
||||
if (checkResp.body['success'] != true) throw TrackNotFoundError(track);
|
||||
|
||||
return NeteaseSourcedTrack(
|
||||
siblings: [],
|
||||
@@ -155,8 +167,11 @@ class NeteaseSourcedTrack extends SourcedTrack {
|
||||
final query = SourcedTrack.getSearchTerm(track);
|
||||
|
||||
final client = getClient();
|
||||
final resp =
|
||||
await client.get('/search?keywords=${Uri.encodeComponent(query)}');
|
||||
final resp = await client.get(
|
||||
'/search?keywords=${Uri.encodeComponent(query)}&realIP=${NeteaseSourcedTrack.lookupRealIp()}',
|
||||
);
|
||||
if (resp.body?['code'] == 405) throw TrackNotFoundError(track);
|
||||
print(resp.body);
|
||||
final results = resp.body['result']['songs'];
|
||||
|
||||
// We can just trust netease music for now
|
||||
@@ -202,7 +217,7 @@ class NeteaseSourcedTrack extends SourcedTrack {
|
||||
|
||||
final client = getClient();
|
||||
final resp = await client.get('/song/detail?ids=${newSourceInfo.id}');
|
||||
final item = resp.body['songs'][0];
|
||||
final item = (resp.body['songs'] as List<dynamic>).first;
|
||||
|
||||
final (:info, :source) = toSiblingType(item);
|
||||
|
||||
@@ -227,25 +242,29 @@ class NeteaseSourcedTrack extends SourcedTrack {
|
||||
);
|
||||
}
|
||||
|
||||
static SiblingType toSiblingType(dynamic item) {
|
||||
static NeteaseSourceInfo toSourceInfo(dynamic item) {
|
||||
final firstArtist = item['ar'] != null ? item['ar'][0] : item['artists'][0];
|
||||
|
||||
return NeteaseSourceInfo(
|
||||
id: item['id'].toString(),
|
||||
artist: item['ar'] != null
|
||||
? item['ar'].map((x) => x['name']).join(',')
|
||||
: item['artists'].map((x) => x['name']).toString(),
|
||||
artistUrl: 'https://music.163.com/#/artist?id=${firstArtist['id']}',
|
||||
pageUrl: 'https://music.163.com/#/song?id=${item['id']}',
|
||||
thumbnail: item['al']?['picUrl'] ??
|
||||
'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
|
||||
title: item['name'],
|
||||
duration: item['dt'] != null
|
||||
? Duration(milliseconds: item['dt'])
|
||||
: Duration(milliseconds: item['duration']),
|
||||
album: item['al']?['name'],
|
||||
);
|
||||
}
|
||||
|
||||
static SiblingType toSiblingType(dynamic item) {
|
||||
final SiblingType sibling = (
|
||||
info: NeteaseSourceInfo(
|
||||
id: item['id'].toString(),
|
||||
artist: item['ar'] != null
|
||||
? item['ar'].map((x) => x['name']).join(',')
|
||||
: item['artists'].map((x) => x['name']).toString(),
|
||||
artistUrl: 'https://music.163.com/#/artist?id=${firstArtist['id']}',
|
||||
pageUrl: 'https://music.163.com/#/song?id=${item['id']}',
|
||||
thumbnail: item['al']?['picUrl'] ??
|
||||
'https://p1.music.126.net/6y-UleORITEDbvrOLV0Q8A==/5639395138885805.jpg',
|
||||
title: item['name'],
|
||||
duration: item['dt'] != null
|
||||
? Duration(milliseconds: item['dt'])
|
||||
: Duration(milliseconds: item['duration']),
|
||||
album: item['al']?['name'],
|
||||
),
|
||||
info: toSourceInfo(item),
|
||||
source: toSourceMap(item),
|
||||
);
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
||||
sourceId: siblings.first.info.id,
|
||||
sourceType: const Value(SourceType.youtube),
|
||||
),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
);
|
||||
|
||||
return YoutubeSourcedTrack(
|
||||
|
||||
@@ -5,8 +5,10 @@ import 'package:rhythm_box/platform.dart';
|
||||
class AutoCacheImage extends StatelessWidget {
|
||||
final String url;
|
||||
final double? width, height;
|
||||
final BoxFit? fit;
|
||||
|
||||
const AutoCacheImage(this.url, {super.key, this.width, this.height});
|
||||
const AutoCacheImage(this.url,
|
||||
{super.key, this.width, this.height, this.fit});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -15,12 +17,14 @@ class AutoCacheImage extends StatelessWidget {
|
||||
imageUrl: url,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
);
|
||||
}
|
||||
return Image.network(
|
||||
url,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:rhythm_box/providers/audio_player.dart';
|
||||
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||
import 'package:rhythm_box/services/database/database.dart';
|
||||
import 'package:rhythm_box/services/duration.dart';
|
||||
@@ -89,48 +90,54 @@ class _SiblingTracksState extends State<SiblingTracks> {
|
||||
final preferences = Get.find<UserPreferencesProvider>().state.value;
|
||||
final searchTerm = _searchTermController.text.trim();
|
||||
|
||||
if (preferences.audioSource == AudioSource.youtube ||
|
||||
preferences.audioSource == AudioSource.piped) {
|
||||
final resultsYt = await youtubeClient.search.search(searchTerm.trim());
|
||||
try {
|
||||
if (preferences.audioSource == AudioSource.youtube ||
|
||||
preferences.audioSource == AudioSource.piped) {
|
||||
final resultsYt = await youtubeClient.search.search(searchTerm.trim());
|
||||
|
||||
final searchResults = await Future.wait(
|
||||
resultsYt.map(YoutubeVideoInfo.fromVideo).mapIndexed((i, video) async {
|
||||
final siblingType = await YoutubeSourcedTrack.toSiblingType(i, video);
|
||||
return siblingType.info;
|
||||
}),
|
||||
);
|
||||
final activeSourceInfo = (_activeTrack! as SourcedTrack).sourceInfo;
|
||||
_siblings = List.from(
|
||||
searchResults
|
||||
..removeWhere((element) => element.id == activeSourceInfo.id)
|
||||
..insert(
|
||||
0,
|
||||
activeSourceInfo,
|
||||
),
|
||||
growable: true,
|
||||
);
|
||||
} else if (preferences.audioSource == AudioSource.netease) {
|
||||
final client = NeteaseSourcedTrack.getClient();
|
||||
final resp = await client
|
||||
.get('/search?keywords=${Uri.encodeComponent(searchTerm)}');
|
||||
final searchResults = resp.body['result']['songs']
|
||||
.map(NeteaseSourcedTrack.toSiblingType)
|
||||
.map((x) => x.info)
|
||||
.toList();
|
||||
final searchResults = await Future.wait(
|
||||
resultsYt
|
||||
.map(YoutubeVideoInfo.fromVideo)
|
||||
.mapIndexed((i, video) async {
|
||||
final siblingType =
|
||||
await YoutubeSourcedTrack.toSiblingType(i, video);
|
||||
return siblingType.info;
|
||||
}),
|
||||
);
|
||||
final activeSourceInfo = (_activeTrack! as SourcedTrack).sourceInfo;
|
||||
_siblings = List.from(
|
||||
searchResults
|
||||
..removeWhere((element) => element.id == activeSourceInfo.id)
|
||||
..insert(
|
||||
0,
|
||||
activeSourceInfo,
|
||||
),
|
||||
growable: true,
|
||||
);
|
||||
} else if (preferences.audioSource == AudioSource.netease) {
|
||||
final client = NeteaseSourcedTrack.getClient();
|
||||
final resp = await client.get(
|
||||
'/search?keywords=${Uri.encodeComponent(searchTerm)}&realIP=${NeteaseSourcedTrack.lookupRealIp()}');
|
||||
final searchResults = resp.body['result']['songs']
|
||||
.map(NeteaseSourcedTrack.toSourceInfo)
|
||||
.toList();
|
||||
|
||||
final activeSourceInfo = (_activeTrack! as SourcedTrack).sourceInfo;
|
||||
_siblings = List.from(
|
||||
searchResults
|
||||
..removeWhere((element) => element.id == activeSourceInfo.id)
|
||||
..insert(
|
||||
0,
|
||||
activeSourceInfo,
|
||||
),
|
||||
growable: true,
|
||||
);
|
||||
final activeSourceInfo = (_activeTrack! as SourcedTrack).sourceInfo;
|
||||
_siblings = List.from(
|
||||
searchResults
|
||||
..removeWhere((element) => element.id == activeSourceInfo.id)
|
||||
..insert(
|
||||
0,
|
||||
activeSourceInfo,
|
||||
),
|
||||
growable: true,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
Get.find<ErrorNotifier>().showError(err.toString());
|
||||
} finally {
|
||||
setState(() => _isSearching = false);
|
||||
}
|
||||
|
||||
setState(() => _isSearching = false);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<string>MainMenu</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.music</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
|
||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+10
|
||||
version: 1.0.0+11
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.0
|
||||
|
||||
Reference in New Issue
Block a user