Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
ad1c188982 | |||
43fae51462 | |||
9012f560b5 | |||
19a7fd82df | |||
010ee6286f | |||
3c3447a9ee | |||
ee2633db52 | |||
ddeda2ce23 | |||
a5f39321eb | |||
da2a3508d1 | |||
ed7b69f7b3 | |||
710ab755fc | |||
4fd9447591 | |||
c97a7ae859 |
@ -8,10 +8,13 @@ Their original app is good enough. But I just want to redesign the user interfac
|
|||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- [x] Playing music
|
- [x] Playing music
|
||||||
- [ ] Add netease music as source
|
- [x] Add netease music as source
|
||||||
|
- [ ] Add bilibili as source
|
||||||
|
- [ ] Add kuwo music as source
|
||||||
|
- [ ] Add kugo music as source
|
||||||
- [x] Re-design user interface
|
- [x] Re-design user interface
|
||||||
- [x] Simplified UI and UX
|
- [x] Simplified UI and UX
|
||||||
- [ ] Support for large screen device
|
- [x] Support for large screen device
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||||
|
@ -57,6 +57,8 @@ PODS:
|
|||||||
- sqlite3/rtree
|
- sqlite3/rtree
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- wakelock_plus (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- audio_service (from `.symlinks/plugins/audio_service/ios`)
|
- audio_service (from `.symlinks/plugins/audio_service/ios`)
|
||||||
@ -76,6 +78,7 @@ DEPENDENCIES:
|
|||||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
@ -117,6 +120,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
wakelock_plus:
|
||||||
|
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
||||||
@ -138,6 +143,7 @@ SPEC CHECKSUMS:
|
|||||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
|
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||||
|
|
||||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
||||||
|
|
||||||
|
@ -1,55 +1,59 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<true/>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>Groovy Box</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>Groovy Box</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>6.0</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>Groovy Box</string>
|
<string>6.0</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundleName</key>
|
||||||
<string>APPL</string>
|
<string>Groovy Box</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>????</string>
|
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
<string>????</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>CFBundleVersion</key>
|
||||||
<true/>
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>LaunchScreen</string>
|
<string>public.app-category.music</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<string>Main</string>
|
<true/>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<array>
|
<string>To provide information for RhythmBox to normalize the output audio</string>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<true/>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<key>UIBackgroundModes</key>
|
||||||
</array>
|
<array>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<string>audio</string>
|
||||||
<array>
|
</array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<string>LaunchScreen</string>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<key>UIMainStoryboardFile</key>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>Main</string>
|
||||||
</array>
|
<key>UIStatusBarHidden</key>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<false/>
|
||||||
<true/>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<array>
|
||||||
<true/>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>public.app-category.music</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
</array>
|
||||||
<string>To provide information for RhythmBox to normalize the output audio</string>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<key>UIStatusBarHidden</key>
|
<array>
|
||||||
<false/>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
</dict>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -8,6 +8,7 @@ import 'package:rhythm_box/providers/audio_player_stream.dart';
|
|||||||
import 'package:rhythm_box/providers/auth.dart';
|
import 'package:rhythm_box/providers/auth.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
import 'package:rhythm_box/providers/endless_playback.dart';
|
import 'package:rhythm_box/providers/endless_playback.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/history.dart';
|
import 'package:rhythm_box/providers/history.dart';
|
||||||
import 'package:rhythm_box/providers/palette.dart';
|
import 'package:rhythm_box/providers/palette.dart';
|
||||||
import 'package:rhythm_box/providers/recent_played.dart';
|
import 'package:rhythm_box/providers/recent_played.dart';
|
||||||
@ -24,11 +25,11 @@ import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
|||||||
import 'package:rhythm_box/services/server/routes/playback.dart';
|
import 'package:rhythm_box/services/server/routes/playback.dart';
|
||||||
import 'package:rhythm_box/services/server/server.dart';
|
import 'package:rhythm_box/services/server/server.dart';
|
||||||
import 'package:rhythm_box/services/server/sourced_track.dart';
|
import 'package:rhythm_box/services/server/sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/wm_tools.dart';
|
||||||
import 'package:rhythm_box/shells/system_shell.dart';
|
import 'package:rhythm_box/shells/system_shell.dart';
|
||||||
import 'package:rhythm_box/translations.dart';
|
import 'package:rhythm_box/translations.dart';
|
||||||
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
||||||
import 'package:smtc_windows/smtc_windows.dart';
|
import 'package:smtc_windows/smtc_windows.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
|
|
||||||
Future<void> main(List<String> rawArgs) async {
|
Future<void> main(List<String> rawArgs) async {
|
||||||
if (rawArgs.contains('web_view_title_bar')) {
|
if (rawArgs.contains('web_view_title_bar')) {
|
||||||
@ -42,13 +43,7 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
if (PlatformInfo.isDesktop) {
|
if (PlatformInfo.isDesktop) {
|
||||||
await windowManager.ensureInitialized();
|
await WindowManagerTools.initialize();
|
||||||
await windowManager.setPreventClose(true);
|
|
||||||
if (PlatformInfo.isMacOS) {
|
|
||||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
|
|
||||||
} else {
|
|
||||||
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (PlatformInfo.isWindows) {
|
if (PlatformInfo.isWindows) {
|
||||||
await SMTCWindows.initialize();
|
await SMTCWindows.initialize();
|
||||||
@ -66,7 +61,7 @@ class RhythmApp extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetMaterialApp.router(
|
return GetMaterialApp.router(
|
||||||
title: 'DietaryGuard',
|
title: 'RhythmBox',
|
||||||
routerDelegate: router.routerDelegate,
|
routerDelegate: router.routerDelegate,
|
||||||
routeInformationParser: router.routeInformationParser,
|
routeInformationParser: router.routeInformationParser,
|
||||||
routeInformationProvider: router.routeInformationProvider,
|
routeInformationProvider: router.routeInformationProvider,
|
||||||
@ -90,8 +85,8 @@ class RhythmApp extends StatelessWidget {
|
|||||||
translations: AppTranslations(),
|
translations: AppTranslations(),
|
||||||
onInit: () => _initializeProviders(context),
|
onInit: () => _initializeProviders(context),
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return SystemShell(
|
return ScaffoldMessenger(
|
||||||
child: ScaffoldMessenger(
|
child: SystemShell(
|
||||||
child: child ?? const SizedBox(),
|
child: child ?? const SizedBox(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -103,6 +98,8 @@ class RhythmApp extends StatelessWidget {
|
|||||||
Get.lazyPut(() => SpotifyProvider());
|
Get.lazyPut(() => SpotifyProvider());
|
||||||
Get.lazyPut(() => SyncedLyricsProvider());
|
Get.lazyPut(() => SyncedLyricsProvider());
|
||||||
|
|
||||||
|
Get.put(ErrorNotifier());
|
||||||
|
|
||||||
Get.put(DatabaseProvider());
|
Get.put(DatabaseProvider());
|
||||||
Get.put(AuthenticationProvider());
|
Get.put(AuthenticationProvider());
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:developer';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/history.dart';
|
import 'package:rhythm_box/providers/history.dart';
|
||||||
import 'package:rhythm_box/providers/palette.dart';
|
import 'package:rhythm_box/providers/palette.dart';
|
||||||
import 'package:rhythm_box/providers/scrobbler.dart';
|
import 'package:rhythm_box/providers/scrobbler.dart';
|
||||||
@ -126,7 +127,8 @@ class AudioPlayerStreamProvider extends GetxController {
|
|||||||
.addTrack(playback.state.value.activeTrack!);
|
.addTrack(playback.state.value.activeTrack!);
|
||||||
lastScrobbled = uid;
|
lastScrobbled = uid;
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[Scrobbler] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Scrobbler] Error: $e', trace: stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
@ -9,6 +8,8 @@ import 'package:rhythm_box/providers/user_preferences.dart';
|
|||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
import 'error_notifier.dart';
|
||||||
|
|
||||||
class EndlessPlaybackProvider extends GetxController {
|
class EndlessPlaybackProvider extends GetxController {
|
||||||
late final _auth = Get.find<AuthenticationProvider>();
|
late final _auth = Get.find<AuthenticationProvider>();
|
||||||
late final _playback = Get.find<AudioPlayerProvider>();
|
late final _playback = Get.find<AudioPlayerProvider>();
|
||||||
@ -88,7 +89,8 @@ class EndlessPlaybackProvider extends GetxController {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[EndlessPlayback] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[EndlessPlayback] Error: $e', trace: stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
38
lib/providers/error_notifier.dart
Normal file
38
lib/providers/error_notifier.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class ErrorNotifier extends GetxController {
|
||||||
|
Rx<MaterialBanner?> showing = Rx(null);
|
||||||
|
|
||||||
|
void logError(String msg, {StackTrace? trace}) {
|
||||||
|
log('$msg${trace != null ? '\nTrace:\n$trace' : ''}');
|
||||||
|
showError(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showError(String msg) {
|
||||||
|
showing.value = MaterialBanner(
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
leading: const Icon(Icons.error),
|
||||||
|
content: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Something went wrong...',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(msg),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
showing.value = null;
|
||||||
|
},
|
||||||
|
child: const Text('Dismiss'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:get/get.dart' hide Value;
|
import 'package:get/get.dart' hide Value;
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/artist.dart';
|
import 'package:rhythm_box/services/artist.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:scrobblenaut/scrobblenaut.dart';
|
import 'package:scrobblenaut/scrobblenaut.dart';
|
||||||
@ -44,7 +44,8 @@ class ScrobblerProvider extends GetxController {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[Scrobble] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Scrobbler] Error: $e', trace: stack);
|
||||||
scrobbler.value = null;
|
scrobbler.value = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -63,8 +64,9 @@ class ScrobblerProvider extends GetxController {
|
|||||||
timestamp: DateTime.now().toUtc(),
|
timestamp: DateTime.now().toUtc(),
|
||||||
trackNumber: track.trackNumber,
|
trackNumber: track.trackNumber,
|
||||||
);
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stack) {
|
||||||
log('[Scrobble] Error: $e; Trace:\n$stackTrace');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Scrobbler] Error: $e', trace: stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/user_preferences.dart';
|
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
@ -72,7 +71,7 @@ Future<List<SkipSegmentTableData>> getAndCacheSkipSegments(String id) async {
|
|||||||
..where((s) => s.trackId.equals(id)))
|
..where((s) => s.trackId.equals(id)))
|
||||||
.get();
|
.get();
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[SkipSegment] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>().logError('[SkipSegment] Error: $e', trace: stack);
|
||||||
return List.castFrom<dynamic, SkipSegmentTableData>([]);
|
return List.castFrom<dynamic, SkipSegmentTableData>([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ import 'package:rhythm_box/services/color.dart';
|
|||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/enums.dart';
|
import 'package:rhythm_box/services/sourced_track/enums.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
|
||||||
typedef UserPreferences = PreferencesTableData;
|
typedef UserPreferences = PreferencesTableData;
|
||||||
|
|
||||||
@ -49,13 +49,7 @@ class UserPreferencesProvider extends GetxController {
|
|||||||
.listen((event) async {
|
.listen((event) async {
|
||||||
state.value = event;
|
state.value = event;
|
||||||
|
|
||||||
if (PlatformInfo.isDesktop) {
|
await WakelockPlus.toggle(enable: state.value.playerWakelock);
|
||||||
await windowManager.setTitleBarStyle(
|
|
||||||
state.value.systemTitleBar
|
|
||||||
? TitleBarStyle.normal
|
|
||||||
: TitleBarStyle.hidden,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await audioPlayer.setAudioNormalization(state.value.normalizeAudio);
|
await audioPlayer.setAudioNormalization(state.value.normalizeAudio);
|
||||||
});
|
});
|
||||||
@ -147,6 +141,10 @@ class UserPreferencesProvider extends GetxController {
|
|||||||
setData(PreferencesTableCompanion(locale: Value(locale)));
|
setData(PreferencesTableCompanion(locale: Value(locale)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setNeteaseApiInstance(String instance) {
|
||||||
|
setData(PreferencesTableCompanion(neteaseApiInstance: Value(instance)));
|
||||||
|
}
|
||||||
|
|
||||||
void setPipedInstance(String instance) {
|
void setPipedInstance(String instance) {
|
||||||
setData(PreferencesTableCompanion(pipedInstance: Value(instance)));
|
setData(PreferencesTableCompanion(pipedInstance: Value(instance)));
|
||||||
}
|
}
|
||||||
@ -167,14 +165,6 @@ class UserPreferencesProvider extends GetxController {
|
|||||||
setData(PreferencesTableCompanion(systemTitleBar: Value(isSystemTitleBar)));
|
setData(PreferencesTableCompanion(systemTitleBar: Value(isSystemTitleBar)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDiscordPresence(bool discordPresence) {
|
|
||||||
setData(PreferencesTableCompanion(discordPresence: Value(discordPresence)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAmoledDarkTheme(bool isAmoled) {
|
|
||||||
setData(PreferencesTableCompanion(amoledDarkTheme: Value(isAmoled)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNormalizeAudio(bool normalize) {
|
void setNormalizeAudio(bool normalize) {
|
||||||
setData(PreferencesTableCompanion(normalizeAudio: Value(normalize)));
|
setData(PreferencesTableCompanion(normalizeAudio: Value(normalize)));
|
||||||
audioPlayer.setAudioNormalization(normalize);
|
audioPlayer.setAudioNormalization(normalize);
|
||||||
@ -184,7 +174,8 @@ class UserPreferencesProvider extends GetxController {
|
|||||||
setData(PreferencesTableCompanion(endlessPlayback: Value(endless)));
|
setData(PreferencesTableCompanion(endlessPlayback: Value(endless)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setEnableConnect(bool enable) {
|
void setPlayerWakelock(bool wakelock) {
|
||||||
setData(PreferencesTableCompanion(enableConnect: Value(enable)));
|
setData(PreferencesTableCompanion(playerWakelock: Value(wakelock)));
|
||||||
|
WakelockPlus.toggle(enable: wakelock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,30 +42,50 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
|
|
||||||
_featuredPlaylist =
|
_featuredPlaylist =
|
||||||
(await _spotify.api.playlists.featured.getPage(20)).items!.toList();
|
(await _spotify.api.playlists.featured.getPage(20)).items!.toList();
|
||||||
setState(() => _isLoading['featured'] = false);
|
if (mounted) {
|
||||||
|
setState(() => _isLoading['featured'] = false);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final idxList = Set();
|
||||||
_recentlyPlaylist = (await _history.fetch())
|
_recentlyPlaylist = (await _history.fetch())
|
||||||
.where((x) => x.playlist != null)
|
.where((x) => x.playlist != null)
|
||||||
.map((x) => x.playlist!)
|
.map((x) => x.playlist!)
|
||||||
.toList();
|
.toList()
|
||||||
setState(() => _isLoading['recently'] = false);
|
..retainWhere((x) => idxList.add(x.id!));
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isLoading['recently'] = false);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_newReleasesPlaylist =
|
_newReleasesPlaylist =
|
||||||
(await _spotify.api.browse.newReleases(country: market).getPage(20))
|
(await _spotify.api.browse.newReleases(country: market).getPage(20))
|
||||||
.items
|
.items
|
||||||
?.map((album) => album.toAlbum())
|
?.map((album) => album.toAlbum())
|
||||||
.toList();
|
.toList();
|
||||||
setState(() => _isLoading['newReleases'] = false);
|
if (mounted) {
|
||||||
|
setState(() => _isLoading['newReleases'] = false);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final customEndpoint =
|
if (_auth.auth.value != null) {
|
||||||
CustomSpotifyEndpoints(_auth.auth.value?.accessToken.value ?? '');
|
final customEndpoint =
|
||||||
final forYouView = await customEndpoint.getView(
|
CustomSpotifyEndpoints(_auth.auth.value?.accessToken.value ?? '');
|
||||||
'made-for-x-hub',
|
final forYouView = await customEndpoint.getView(
|
||||||
market: market,
|
'made-for-x-hub',
|
||||||
locale: Intl.canonicalizedLocale(locale.toString()),
|
market: market,
|
||||||
);
|
locale: Intl.canonicalizedLocale(locale.toString()),
|
||||||
_forYouView = forYouView['content']?['items'];
|
);
|
||||||
setState(() => _isLoading['forYou'] = false);
|
_forYouView = forYouView['content']?['items'];
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _isLoading['forYou'] = false);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -85,15 +105,6 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
),
|
),
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
if (_newReleasesPlaylist?.isNotEmpty ?? false)
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: PlaylistSection(
|
|
||||||
isLoading: _isLoading['newReleases']!,
|
|
||||||
title: 'New Releases',
|
|
||||||
list: _newReleasesPlaylist,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_newReleasesPlaylist?.isNotEmpty ?? false) const SliverGap(16),
|
|
||||||
if (_recentlyPlaylist?.isNotEmpty ?? false)
|
if (_recentlyPlaylist?.isNotEmpty ?? false)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: PlaylistSection(
|
child: PlaylistSection(
|
||||||
@ -103,6 +114,15 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_recentlyPlaylist?.isNotEmpty ?? false) const SliverGap(16),
|
if (_recentlyPlaylist?.isNotEmpty ?? false) const SliverGap(16),
|
||||||
|
if (_newReleasesPlaylist?.isNotEmpty ?? false)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: PlaylistSection(
|
||||||
|
isLoading: _isLoading['newReleases']!,
|
||||||
|
title: 'New Releases',
|
||||||
|
list: _newReleasesPlaylist,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_newReleasesPlaylist?.isNotEmpty ?? false) const SliverGap(16),
|
||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemCount: _forYouView?.length ?? 0,
|
itemCount: _forYouView?.length ?? 0,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
|
@ -5,9 +5,14 @@ import 'package:get/get.dart';
|
|||||||
import 'package:rhythm_box/widgets/lyrics/synced_lyrics.dart';
|
import 'package:rhythm_box/widgets/lyrics/synced_lyrics.dart';
|
||||||
import 'package:rhythm_box/widgets/player/bottom_player.dart';
|
import 'package:rhythm_box/widgets/player/bottom_player.dart';
|
||||||
|
|
||||||
class LyricsScreen extends StatelessWidget {
|
class LyricsScreen extends StatefulWidget {
|
||||||
const LyricsScreen({super.key});
|
const LyricsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LyricsScreen> createState() => _LyricsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
|
@ -2,8 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/screens/player/queue.dart';
|
||||||
|
import 'package:rhythm_box/screens/player/siblings.dart';
|
||||||
import 'package:rhythm_box/widgets/lyrics/synced_lyrics.dart';
|
import 'package:rhythm_box/widgets/lyrics/synced_lyrics.dart';
|
||||||
import 'package:rhythm_box/widgets/player/bottom_player.dart';
|
import 'package:rhythm_box/widgets/player/bottom_player.dart';
|
||||||
|
import 'package:rhythm_box/widgets/player/devices.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
class MiniPlayerScreen extends StatefulWidget {
|
class MiniPlayerScreen extends StatefulWidget {
|
||||||
@ -94,6 +97,46 @@ class _MiniPlayerScreenState extends State<MiniPlayerScreen> {
|
|||||||
onPressed: () => _exitMiniPlayer(),
|
onPressed: () => _exitMiniPlayer(),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.speaker, size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const PlayerDevicePopup(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.merge),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const SiblingTracksPopup(),
|
||||||
|
).then((_) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.queue_music),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const PlayerQueuePopup(),
|
||||||
|
).then((_) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: _isHoverMode
|
icon: _isHoverMode
|
||||||
? const Icon(Icons.touch_app)
|
? const Icon(Icons.touch_app)
|
||||||
|
32
lib/screens/player/source_details.dart
Normal file
32
lib/screens/player/source_details.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/widgets/player/track_source_details.dart';
|
||||||
|
|
||||||
|
class SourceDetailsPopup extends StatelessWidget {
|
||||||
|
const SourceDetailsPopup({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ActiveSourcedTrackProvider activeTrack = Get.find();
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Source Details',
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(
|
||||||
|
() => TrackSourceDetails(
|
||||||
|
track: activeTrack.state.value!,
|
||||||
|
).paddingSymmetric(horizontal: 24),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import 'package:rhythm_box/providers/audio_player.dart';
|
|||||||
import 'package:rhythm_box/providers/auth.dart';
|
import 'package:rhythm_box/providers/auth.dart';
|
||||||
import 'package:rhythm_box/screens/player/queue.dart';
|
import 'package:rhythm_box/screens/player/queue.dart';
|
||||||
import 'package:rhythm_box/screens/player/siblings.dart';
|
import 'package:rhythm_box/screens/player/siblings.dart';
|
||||||
|
import 'package:rhythm_box/screens/player/source_details.dart';
|
||||||
import 'package:rhythm_box/services/artist.dart';
|
import 'package:rhythm_box/services/artist.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:rhythm_box/services/duration.dart';
|
import 'package:rhythm_box/services/duration.dart';
|
||||||
@ -69,294 +70,337 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
direction: DismissiblePageDismissDirection.down,
|
direction: DismissiblePageDismissDirection.down,
|
||||||
child: Material(
|
child: Scaffold(
|
||||||
color: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
child: SafeArea(
|
body: SafeArea(
|
||||||
child: Row(
|
child: Center(
|
||||||
children: [
|
child: Row(
|
||||||
Expanded(
|
children: [
|
||||||
child: ListView(
|
Expanded(
|
||||||
shrinkWrap: true,
|
child: ListView(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
shrinkWrap: true,
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||||
Obx(
|
children: [
|
||||||
() => LimitedBox(
|
Obx(
|
||||||
maxHeight: maxAlbumSize,
|
() => LimitedBox(
|
||||||
maxWidth: maxAlbumSize,
|
maxHeight: maxAlbumSize,
|
||||||
child: Hero(
|
maxWidth: maxAlbumSize,
|
||||||
tag: const Key('current-active-track-album-art'),
|
child: Hero(
|
||||||
child: ClipRRect(
|
tag: const Key('current-active-track-album-art'),
|
||||||
borderRadius:
|
|
||||||
const BorderRadius.all(Radius.circular(16)),
|
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: _albumArt != null
|
child: ClipRRect(
|
||||||
? AutoCacheImage(
|
borderRadius:
|
||||||
_albumArt!,
|
const BorderRadius.all(Radius.circular(16)),
|
||||||
width: albumSize,
|
child: _albumArt != null
|
||||||
height: albumSize,
|
? AutoCacheImage(
|
||||||
)
|
_albumArt!,
|
||||||
: Container(
|
width: albumSize,
|
||||||
color: Theme.of(context)
|
height: albumSize,
|
||||||
.colorScheme
|
)
|
||||||
.surfaceContainerHigh,
|
: Container(
|
||||||
width: 64,
|
color: Theme.of(context)
|
||||||
height: 64,
|
.colorScheme
|
||||||
child: const Center(
|
.surfaceContainerHigh,
|
||||||
child: Icon(Icons.image)),
|
width: 64,
|
||||||
),
|
height: 64,
|
||||||
),
|
child: const Center(
|
||||||
).marginSymmetric(horizontal: 24),
|
child: Icon(Icons.image),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).marginSymmetric(horizontal: 24),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const Gap(24),
|
||||||
const Gap(24),
|
Obx(
|
||||||
Obx(
|
() => Row(
|
||||||
() => Row(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
children: [
|
Expanded(
|
||||||
Expanded(
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
|
Text(
|
||||||
|
_playback.state.value.activeTrack?.name ??
|
||||||
|
'Not playing',
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleLarge,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_playback.state.value.activeTrack?.artists
|
||||||
|
?.asString() ??
|
||||||
|
'No author',
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.bodyMedium,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_playback.state.value.activeTrack != null &&
|
||||||
|
_auth.auth.value != null)
|
||||||
|
TrackHeartButton(
|
||||||
|
key: ValueKey(
|
||||||
|
_playback.state.value.activeTrack!.id!,
|
||||||
|
),
|
||||||
|
trackId: _playback.state.value.activeTrack!.id!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 32),
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Obx(
|
||||||
|
() => Column(
|
||||||
|
children: [
|
||||||
|
SliderTheme(
|
||||||
|
data: SliderThemeData(
|
||||||
|
trackHeight: 2,
|
||||||
|
trackShape: _PlayerProgressTrackShape(),
|
||||||
|
thumbShape: const RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 8,
|
||||||
|
),
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
secondaryTrackValue: _playback
|
||||||
|
.durationBuffered.value.inMilliseconds
|
||||||
|
.abs()
|
||||||
|
.toDouble(),
|
||||||
|
value: _draggingValue?.abs() ??
|
||||||
|
_playback
|
||||||
|
.durationCurrent.value.inMilliseconds
|
||||||
|
.toDouble()
|
||||||
|
.abs(),
|
||||||
|
min: 0,
|
||||||
|
max: max(
|
||||||
|
_playback.durationCurrent.value.inMilliseconds
|
||||||
|
.abs(),
|
||||||
|
_playback.durationTotal.value.inMilliseconds
|
||||||
|
.abs(),
|
||||||
|
).toDouble(),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _draggingValue = value);
|
||||||
|
},
|
||||||
|
onChangeEnd: (value) {
|
||||||
|
audioPlayer.seek(
|
||||||
|
Duration(milliseconds: value.toInt()),
|
||||||
|
);
|
||||||
|
setState(() => _draggingValue = null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_playback.state.value.activeTrack?.name ??
|
_playback.durationCurrent.value
|
||||||
'Not playing',
|
.toHumanReadableString(),
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
textAlign: TextAlign.left,
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_playback.state.value.activeTrack?.artists
|
_playback.durationTotal.value
|
||||||
?.asString() ??
|
.toHumanReadableString(),
|
||||||
'No author',
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
).paddingSymmetric(horizontal: 8, vertical: 4),
|
||||||
),
|
],
|
||||||
if (_playback.state.value.activeTrack != null &&
|
).paddingSymmetric(horizontal: 24),
|
||||||
_auth.auth.value != null)
|
),
|
||||||
TrackHeartButton(
|
const Gap(24),
|
||||||
trackId: _playback.state.value.activeTrack!.id!,
|
Row(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
],
|
|
||||||
).paddingSymmetric(horizontal: 32),
|
|
||||||
),
|
|
||||||
const Gap(24),
|
|
||||||
Obx(
|
|
||||||
() => Column(
|
|
||||||
children: [
|
children: [
|
||||||
SliderTheme(
|
StreamBuilder<bool>(
|
||||||
data: SliderThemeData(
|
stream: audioPlayer.shuffledStream,
|
||||||
trackHeight: 2,
|
builder: (context, snapshot) {
|
||||||
trackShape: _PlayerProgressTrackShape(),
|
final shuffled = snapshot.data ?? false;
|
||||||
thumbShape: const RoundSliderThumbShape(
|
return Obx(
|
||||||
enabledThumbRadius: 8,
|
() => IconButton(
|
||||||
),
|
icon: Icon(
|
||||||
overlayShape: SliderComponentShape.noOverlay,
|
shuffled
|
||||||
),
|
? Icons.shuffle_on_outlined
|
||||||
child: Slider(
|
: Icons.shuffle,
|
||||||
secondaryTrackValue: _playback
|
),
|
||||||
.durationBuffered.value.inMilliseconds
|
onPressed: _isFetchingActiveTrack
|
||||||
.abs()
|
? null
|
||||||
.toDouble(),
|
: () {
|
||||||
value: _draggingValue?.abs() ??
|
if (shuffled) {
|
||||||
_playback.durationCurrent.value.inMilliseconds
|
audioPlayer.setShuffle(false);
|
||||||
.toDouble()
|
} else {
|
||||||
.abs(),
|
audioPlayer.setShuffle(true);
|
||||||
min: 0,
|
}
|
||||||
max: max(
|
},
|
||||||
_playback.durationCurrent.value.inMilliseconds
|
),
|
||||||
.abs(),
|
);
|
||||||
_playback.durationTotal.value.inMilliseconds
|
},
|
||||||
.abs(),
|
),
|
||||||
).toDouble(),
|
Obx(
|
||||||
onChanged: (value) {
|
() => IconButton(
|
||||||
setState(() => _draggingValue = value);
|
icon: const Icon(Icons.skip_previous),
|
||||||
},
|
onPressed: _isFetchingActiveTrack
|
||||||
onChangeEnd: (value) {
|
? null
|
||||||
audioPlayer.seek(
|
: audioPlayer.skipToPrevious,
|
||||||
Duration(milliseconds: value.toInt()));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
const Gap(8),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Obx(
|
||||||
children: [
|
() => SizedBox(
|
||||||
Text(
|
width: 56,
|
||||||
_playback.durationCurrent.value
|
height: 56,
|
||||||
.toHumanReadableString(),
|
child: IconButton.filled(
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
icon: _isFetchingActiveTrack
|
||||||
|
? const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Icon(
|
||||||
|
!_isPlaying
|
||||||
|
? Icons.play_arrow
|
||||||
|
: Icons.pause,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
onPressed: _togglePlayState,
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
_playback.durationTotal.value
|
),
|
||||||
.toHumanReadableString(),
|
const Gap(8),
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
Obx(
|
||||||
),
|
() => IconButton(
|
||||||
],
|
icon: const Icon(Icons.skip_next),
|
||||||
).paddingSymmetric(horizontal: 8, vertical: 4),
|
onPressed: _isFetchingActiveTrack
|
||||||
],
|
? null
|
||||||
).paddingSymmetric(horizontal: 24),
|
: audioPlayer.skipToNext,
|
||||||
),
|
),
|
||||||
const Gap(24),
|
),
|
||||||
Row(
|
Obx(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
() => IconButton(
|
||||||
children: [
|
|
||||||
StreamBuilder<bool>(
|
|
||||||
stream: audioPlayer.shuffledStream,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final shuffled = snapshot.data ?? false;
|
|
||||||
return IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
shuffled
|
_loopMode == PlaylistMode.none
|
||||||
? Icons.shuffle_on_outlined
|
? Icons.repeat
|
||||||
: Icons.shuffle,
|
: _loopMode == PlaylistMode.loop
|
||||||
|
? Icons.repeat_on_outlined
|
||||||
|
: Icons.repeat_one_on_outlined,
|
||||||
),
|
),
|
||||||
onPressed: _isFetchingActiveTrack
|
onPressed: _isFetchingActiveTrack
|
||||||
? null
|
? null
|
||||||
: () {
|
: () async {
|
||||||
if (shuffled) {
|
await audioPlayer.setLoopMode(
|
||||||
audioPlayer.setShuffle(false);
|
switch (_loopMode) {
|
||||||
} else {
|
PlaylistMode.loop =>
|
||||||
audioPlayer.setShuffle(true);
|
PlaylistMode.single,
|
||||||
}
|
PlaylistMode.single =>
|
||||||
|
PlaylistMode.none,
|
||||||
|
PlaylistMode.none =>
|
||||||
|
PlaylistMode.loop,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Obx(
|
|
||||||
() => IconButton(
|
|
||||||
icon: const Icon(Icons.skip_previous),
|
|
||||||
onPressed: _isFetchingActiveTrack
|
|
||||||
? null
|
|
||||||
: audioPlayer.skipToPrevious,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
Obx(
|
|
||||||
() => SizedBox(
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
child: IconButton.filled(
|
|
||||||
icon: (_isFetchingActiveTrack && _isPlaying)
|
|
||||||
? const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: Colors.white,
|
|
||||||
strokeWidth: 2.5,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Icon(
|
|
||||||
!_isPlaying
|
|
||||||
? Icons.play_arrow
|
|
||||||
: Icons.pause,
|
|
||||||
size: 28,
|
|
||||||
),
|
|
||||||
onPressed: _togglePlayState,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
const Gap(8),
|
),
|
||||||
Obx(
|
const Gap(20),
|
||||||
() => IconButton(
|
SizedBox(
|
||||||
icon: const Icon(Icons.skip_next),
|
height: 40,
|
||||||
onPressed: _isFetchingActiveTrack
|
child: ListView(
|
||||||
? null
|
scrollDirection: Axis.horizontal,
|
||||||
: audioPlayer.skipToNext,
|
children: [
|
||||||
),
|
TextButton.icon(
|
||||||
),
|
icon: const Icon(Icons.queue_music),
|
||||||
Obx(
|
label: const Text(
|
||||||
() => IconButton(
|
'Queue',
|
||||||
icon: Icon(
|
maxLines: 1,
|
||||||
_loopMode == PlaylistMode.none
|
overflow: TextOverflow.fade,
|
||||||
? Icons.repeat
|
),
|
||||||
: _loopMode == PlaylistMode.loop
|
|
||||||
? Icons.repeat_on_outlined
|
|
||||||
: Icons.repeat_one_on_outlined,
|
|
||||||
),
|
|
||||||
onPressed: _isFetchingActiveTrack
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
await audioPlayer.setLoopMode(
|
|
||||||
switch (_loopMode) {
|
|
||||||
PlaylistMode.loop =>
|
|
||||||
PlaylistMode.single,
|
|
||||||
PlaylistMode.single =>
|
|
||||||
PlaylistMode.none,
|
|
||||||
PlaylistMode.none => PlaylistMode.loop,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Gap(20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TextButton.icon(
|
|
||||||
icon: const Icon(Icons.queue_music),
|
|
||||||
label: const Text('Queue'),
|
|
||||||
onPressed: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
useRootNavigator: true,
|
|
||||||
isScrollControlled: true,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => const PlayerQueuePopup(),
|
|
||||||
).then((_) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!isLargeScreen) const Gap(4),
|
|
||||||
if (!isLargeScreen)
|
|
||||||
Expanded(
|
|
||||||
child: TextButton.icon(
|
|
||||||
icon: const Icon(Icons.lyrics),
|
|
||||||
label: const Text('Lyrics'),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
GoRouter.of(context).pushNamed('playerLyrics');
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) =>
|
||||||
|
const PlayerQueuePopup(),
|
||||||
|
).then((_) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
if (!isLargeScreen) const Gap(4),
|
||||||
const Gap(4),
|
if (!isLargeScreen)
|
||||||
Expanded(
|
TextButton.icon(
|
||||||
child: TextButton.icon(
|
icon: const Icon(Icons.lyrics),
|
||||||
icon: const Icon(Icons.merge),
|
label: const Text(
|
||||||
label: const Text('Sources'),
|
'Lyrics',
|
||||||
onPressed: () {
|
maxLines: 1,
|
||||||
showModalBottomSheet(
|
overflow: TextOverflow.fade,
|
||||||
useRootNavigator: true,
|
),
|
||||||
isScrollControlled: true,
|
onPressed: () {
|
||||||
context: context,
|
GoRouter.of(context)
|
||||||
builder: (context) =>
|
.pushNamed('playerLyrics');
|
||||||
const SiblingTracksPopup(),
|
},
|
||||||
).then((_) {
|
),
|
||||||
if (mounted) {
|
const Gap(4),
|
||||||
setState(() {});
|
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(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
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(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
if (isLargeScreen) const Gap(24),
|
||||||
if (isLargeScreen) const Gap(24),
|
if (isLargeScreen)
|
||||||
if (isLargeScreen)
|
const Expanded(
|
||||||
const Expanded(
|
child: SyncedLyrics(defaultTextZoom: 67),
|
||||||
child: SyncedLyrics(defaultTextZoom: 67),
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
).marginSymmetric(horizontal: 24),
|
).marginSymmetric(horizontal: 24),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -5,6 +6,7 @@ import 'package:rhythm_box/providers/auth.dart';
|
|||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/providers/user_preferences.dart';
|
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||||
import 'package:rhythm_box/screens/auth/login.dart';
|
import 'package:rhythm_box/screens/auth/login.dart';
|
||||||
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
||||||
import 'package:rhythm_box/widgets/sized_container.dart';
|
import 'package:rhythm_box/widgets/sized_container.dart';
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
centerTitle: MediaQuery.of(context).size.width >= 720,
|
centerTitle: MediaQuery.of(context).size.width >= 720,
|
||||||
),
|
),
|
||||||
body: CenteredContainer(
|
body: CenteredContainer(
|
||||||
child: Column(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (_authenticate.auth.value == null) {
|
if (_authenticate.auth.value == null) {
|
||||||
@ -101,6 +103,53 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const Divider(thickness: 0.3, height: 1),
|
const Divider(thickness: 0.3, height: 1),
|
||||||
|
Obx(
|
||||||
|
() => ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: const Icon(Icons.audio_file),
|
||||||
|
title: const Text('Audio Source'),
|
||||||
|
subtitle:
|
||||||
|
const Text('Choose who to provide the songs you played.'),
|
||||||
|
trailing: DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton2<AudioSource>(
|
||||||
|
isExpanded: true,
|
||||||
|
hint: Text(
|
||||||
|
'Select Item',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
items: AudioSource.values
|
||||||
|
.map((AudioSource item) =>
|
||||||
|
DropdownMenuItem<AudioSource>(
|
||||||
|
value: item,
|
||||||
|
child: Text(
|
||||||
|
item.label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
value: _preferences.state.value.audioSource,
|
||||||
|
onChanged: (AudioSource? value) {
|
||||||
|
_preferences
|
||||||
|
.setAudioSource(value ?? AudioSource.youtube);
|
||||||
|
},
|
||||||
|
buttonStyleData: const ButtonStyleData(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
height: 40,
|
||||||
|
width: 140,
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(thickness: 0.3, height: 1),
|
||||||
Obx(
|
Obx(
|
||||||
() => SwitchListTile(
|
() => SwitchListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
@ -123,6 +172,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
onChanged: _preferences.setNormalizeAudio,
|
onChanged: _preferences.setNormalizeAudio,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Obx(
|
||||||
|
() => SwitchListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
secondary: const Icon(Icons.screen_lock_portrait),
|
||||||
|
title: const Text('Player Wakelock'),
|
||||||
|
subtitle: const Text(
|
||||||
|
'Keep your screen doesn\'t lock in player screen'),
|
||||||
|
value: _preferences.state.value.playerWakelock,
|
||||||
|
onChanged: _preferences.setPlayerWakelock,
|
||||||
|
),
|
||||||
|
),
|
||||||
const Divider(thickness: 0.3, height: 1),
|
const Divider(thickness: 0.3, height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/local_track.dart';
|
import 'package:rhythm_box/services/local_track.dart';
|
||||||
import 'package:rhythm_box/services/server/server.dart';
|
import 'package:rhythm_box/services/server/server.dart';
|
||||||
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
||||||
@ -93,7 +94,7 @@ abstract class AudioPlayerInterface {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_mkPlayer.stream.error.listen((event) {
|
_mkPlayer.stream.error.listen((event) {
|
||||||
log('[Playback] Error: $event');
|
Get.find<ErrorNotifier>().logError('[Playback][Player] Error: $event');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,16 +90,28 @@ class RhythmAudioPlayer extends AudioPlayerInterface
|
|||||||
|
|
||||||
Future<void> skipToNext() async {
|
Future<void> skipToNext() async {
|
||||||
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
||||||
|
Get.find<AudioPlayerProvider>().durationBuffered.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
|
Get.find<AudioPlayerProvider>().durationCurrent.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
await _mkPlayer.next();
|
await _mkPlayer.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> skipToPrevious() async {
|
Future<void> skipToPrevious() async {
|
||||||
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
||||||
|
Get.find<AudioPlayerProvider>().durationBuffered.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
|
Get.find<AudioPlayerProvider>().durationCurrent.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
await _mkPlayer.previous();
|
await _mkPlayer.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> jumpTo(int index) async {
|
Future<void> jumpTo(int index) async {
|
||||||
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
||||||
|
Get.find<AudioPlayerProvider>().durationBuffered.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
|
Get.find<AudioPlayerProvider>().durationCurrent.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
await _mkPlayer.jump(index);
|
await _mkPlayer.jump(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'package:get/get.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:flutter_broadcasts/flutter_broadcasts.dart';
|
import 'package:flutter_broadcasts/flutter_broadcasts.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
|
|
||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:rhythm_box/services/audio_player/playback_state.dart';
|
import 'package:rhythm_box/services/audio_player/playback_state.dart';
|
||||||
@ -49,7 +50,8 @@ class CustomPlayer extends Player {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
stream.error.listen((event) {
|
stream.error.listen((event) {
|
||||||
log('[MediaKitError] $event');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Playback][CustomLayer] Error: $event');
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
PackageInfo.fromPlatform().then((packageInfo) {
|
PackageInfo.fromPlatform().then((packageInfo) {
|
||||||
|
@ -55,7 +55,21 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
AppDatabase() : super(_openConnection());
|
AppDatabase() : super(_openConnection());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 1;
|
int get schemaVersion => 2;
|
||||||
|
|
||||||
|
@override
|
||||||
|
MigrationStrategy get migration {
|
||||||
|
return MigrationStrategy(
|
||||||
|
onCreate: (Migrator m) async {
|
||||||
|
await m.createAll();
|
||||||
|
},
|
||||||
|
onUpgrade: (Migrator m, int from, int to) async {
|
||||||
|
if (from < 2) {
|
||||||
|
await m.addColumn(preferencesTable, preferencesTable.playerWakelock);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyDatabase _openConnection() {
|
LazyDatabase _openConnection() {
|
||||||
|
@ -317,16 +317,6 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
'CHECK ("album_color_sync" IN (0, 1))'),
|
'CHECK ("album_color_sync" IN (0, 1))'),
|
||||||
defaultValue: const Constant(true));
|
defaultValue: const Constant(true));
|
||||||
static const VerificationMeta _amoledDarkThemeMeta =
|
|
||||||
const VerificationMeta('amoledDarkTheme');
|
|
||||||
@override
|
|
||||||
late final GeneratedColumn<bool> amoledDarkTheme = GeneratedColumn<bool>(
|
|
||||||
'amoled_dark_theme', aliasedName, false,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("amoled_dark_theme" IN (0, 1))'),
|
|
||||||
defaultValue: const Constant(false));
|
|
||||||
static const VerificationMeta _checkUpdateMeta =
|
static const VerificationMeta _checkUpdateMeta =
|
||||||
const VerificationMeta('checkUpdate');
|
const VerificationMeta('checkUpdate');
|
||||||
@override
|
@override
|
||||||
@ -463,6 +453,15 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultValue: const Constant('https://pipedapi.kavin.rocks'));
|
defaultValue: const Constant('https://pipedapi.kavin.rocks'));
|
||||||
|
static const VerificationMeta _neteaseApiInstanceMeta =
|
||||||
|
const VerificationMeta('neteaseApiInstance');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<String> neteaseApiInstance =
|
||||||
|
GeneratedColumn<String>('netease_api_instance', aliasedName, false,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultValue:
|
||||||
|
const Constant('https://rhythmbox-netease-music-api.vercel.app'));
|
||||||
static const VerificationMeta _themeModeMeta =
|
static const VerificationMeta _themeModeMeta =
|
||||||
const VerificationMeta('themeMode');
|
const VerificationMeta('themeMode');
|
||||||
@override
|
@override
|
||||||
@ -504,16 +503,6 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
defaultValue: Constant(SourceCodecs.m4a.name))
|
defaultValue: Constant(SourceCodecs.m4a.name))
|
||||||
.withConverter<SourceCodecs>(
|
.withConverter<SourceCodecs>(
|
||||||
$PreferencesTableTable.$converterdownloadMusicCodec);
|
$PreferencesTableTable.$converterdownloadMusicCodec);
|
||||||
static const VerificationMeta _discordPresenceMeta =
|
|
||||||
const VerificationMeta('discordPresence');
|
|
||||||
@override
|
|
||||||
late final GeneratedColumn<bool> discordPresence = GeneratedColumn<bool>(
|
|
||||||
'discord_presence', aliasedName, false,
|
|
||||||
type: DriftSqlType.bool,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("discord_presence" IN (0, 1))'),
|
|
||||||
defaultValue: const Constant(true));
|
|
||||||
static const VerificationMeta _endlessPlaybackMeta =
|
static const VerificationMeta _endlessPlaybackMeta =
|
||||||
const VerificationMeta('endlessPlayback');
|
const VerificationMeta('endlessPlayback');
|
||||||
@override
|
@override
|
||||||
@ -524,22 +513,21 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
'CHECK ("endless_playback" IN (0, 1))'),
|
'CHECK ("endless_playback" IN (0, 1))'),
|
||||||
defaultValue: const Constant(true));
|
defaultValue: const Constant(true));
|
||||||
static const VerificationMeta _enableConnectMeta =
|
static const VerificationMeta _playerWakelockMeta =
|
||||||
const VerificationMeta('enableConnect');
|
const VerificationMeta('playerWakelock');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<bool> enableConnect = GeneratedColumn<bool>(
|
late final GeneratedColumn<bool> playerWakelock = GeneratedColumn<bool>(
|
||||||
'enable_connect', aliasedName, false,
|
'player_wakelock', aliasedName, false,
|
||||||
type: DriftSqlType.bool,
|
type: DriftSqlType.bool,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
'CHECK ("enable_connect" IN (0, 1))'),
|
'CHECK ("player_wakelock" IN (0, 1))'),
|
||||||
defaultValue: const Constant(false));
|
defaultValue: const Constant(true));
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [
|
List<GeneratedColumn> get $columns => [
|
||||||
id,
|
id,
|
||||||
audioQuality,
|
audioQuality,
|
||||||
albumColorSync,
|
albumColorSync,
|
||||||
amoledDarkTheme,
|
|
||||||
checkUpdate,
|
checkUpdate,
|
||||||
normalizeAudio,
|
normalizeAudio,
|
||||||
showSystemTrayIcon,
|
showSystemTrayIcon,
|
||||||
@ -554,13 +542,13 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
downloadLocation,
|
downloadLocation,
|
||||||
localLibraryLocation,
|
localLibraryLocation,
|
||||||
pipedInstance,
|
pipedInstance,
|
||||||
|
neteaseApiInstance,
|
||||||
themeMode,
|
themeMode,
|
||||||
audioSource,
|
audioSource,
|
||||||
streamMusicCodec,
|
streamMusicCodec,
|
||||||
downloadMusicCodec,
|
downloadMusicCodec,
|
||||||
discordPresence,
|
|
||||||
endlessPlayback,
|
endlessPlayback,
|
||||||
enableConnect
|
playerWakelock
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@ -583,12 +571,6 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
albumColorSync.isAcceptableOrUnknown(
|
albumColorSync.isAcceptableOrUnknown(
|
||||||
data['album_color_sync']!, _albumColorSyncMeta));
|
data['album_color_sync']!, _albumColorSyncMeta));
|
||||||
}
|
}
|
||||||
if (data.containsKey('amoled_dark_theme')) {
|
|
||||||
context.handle(
|
|
||||||
_amoledDarkThemeMeta,
|
|
||||||
amoledDarkTheme.isAcceptableOrUnknown(
|
|
||||||
data['amoled_dark_theme']!, _amoledDarkThemeMeta));
|
|
||||||
}
|
|
||||||
if (data.containsKey('check_update')) {
|
if (data.containsKey('check_update')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_checkUpdateMeta,
|
_checkUpdateMeta,
|
||||||
@ -639,27 +621,27 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
pipedInstance.isAcceptableOrUnknown(
|
pipedInstance.isAcceptableOrUnknown(
|
||||||
data['piped_instance']!, _pipedInstanceMeta));
|
data['piped_instance']!, _pipedInstanceMeta));
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('netease_api_instance')) {
|
||||||
|
context.handle(
|
||||||
|
_neteaseApiInstanceMeta,
|
||||||
|
neteaseApiInstance.isAcceptableOrUnknown(
|
||||||
|
data['netease_api_instance']!, _neteaseApiInstanceMeta));
|
||||||
|
}
|
||||||
context.handle(_themeModeMeta, const VerificationResult.success());
|
context.handle(_themeModeMeta, const VerificationResult.success());
|
||||||
context.handle(_audioSourceMeta, const VerificationResult.success());
|
context.handle(_audioSourceMeta, const VerificationResult.success());
|
||||||
context.handle(_streamMusicCodecMeta, const VerificationResult.success());
|
context.handle(_streamMusicCodecMeta, const VerificationResult.success());
|
||||||
context.handle(_downloadMusicCodecMeta, const VerificationResult.success());
|
context.handle(_downloadMusicCodecMeta, const VerificationResult.success());
|
||||||
if (data.containsKey('discord_presence')) {
|
|
||||||
context.handle(
|
|
||||||
_discordPresenceMeta,
|
|
||||||
discordPresence.isAcceptableOrUnknown(
|
|
||||||
data['discord_presence']!, _discordPresenceMeta));
|
|
||||||
}
|
|
||||||
if (data.containsKey('endless_playback')) {
|
if (data.containsKey('endless_playback')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_endlessPlaybackMeta,
|
_endlessPlaybackMeta,
|
||||||
endlessPlayback.isAcceptableOrUnknown(
|
endlessPlayback.isAcceptableOrUnknown(
|
||||||
data['endless_playback']!, _endlessPlaybackMeta));
|
data['endless_playback']!, _endlessPlaybackMeta));
|
||||||
}
|
}
|
||||||
if (data.containsKey('enable_connect')) {
|
if (data.containsKey('player_wakelock')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_enableConnectMeta,
|
_playerWakelockMeta,
|
||||||
enableConnect.isAcceptableOrUnknown(
|
playerWakelock.isAcceptableOrUnknown(
|
||||||
data['enable_connect']!, _enableConnectMeta));
|
data['player_wakelock']!, _playerWakelockMeta));
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@ -677,8 +659,6 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
DriftSqlType.string, data['${effectivePrefix}audio_quality'])!),
|
DriftSqlType.string, data['${effectivePrefix}audio_quality'])!),
|
||||||
albumColorSync: attachedDatabase.typeMapping
|
albumColorSync: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}album_color_sync'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}album_color_sync'])!,
|
||||||
amoledDarkTheme: attachedDatabase.typeMapping.read(
|
|
||||||
DriftSqlType.bool, data['${effectivePrefix}amoled_dark_theme'])!,
|
|
||||||
checkUpdate: attachedDatabase.typeMapping
|
checkUpdate: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}check_update'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}check_update'])!,
|
||||||
normalizeAudio: attachedDatabase.typeMapping
|
normalizeAudio: attachedDatabase.typeMapping
|
||||||
@ -715,6 +695,8 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
data['${effectivePrefix}local_library_location'])!),
|
data['${effectivePrefix}local_library_location'])!),
|
||||||
pipedInstance: attachedDatabase.typeMapping
|
pipedInstance: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}piped_instance'])!,
|
.read(DriftSqlType.string, data['${effectivePrefix}piped_instance'])!,
|
||||||
|
neteaseApiInstance: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string, data['${effectivePrefix}netease_api_instance'])!,
|
||||||
themeMode: $PreferencesTableTable.$converterthemeMode.fromSql(
|
themeMode: $PreferencesTableTable.$converterthemeMode.fromSql(
|
||||||
attachedDatabase.typeMapping.read(
|
attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.string, data['${effectivePrefix}theme_mode'])!),
|
DriftSqlType.string, data['${effectivePrefix}theme_mode'])!),
|
||||||
@ -727,12 +709,10 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
downloadMusicCodec: $PreferencesTableTable.$converterdownloadMusicCodec
|
downloadMusicCodec: $PreferencesTableTable.$converterdownloadMusicCodec
|
||||||
.fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string,
|
.fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string,
|
||||||
data['${effectivePrefix}download_music_codec'])!),
|
data['${effectivePrefix}download_music_codec'])!),
|
||||||
discordPresence: attachedDatabase.typeMapping
|
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}discord_presence'])!,
|
|
||||||
endlessPlayback: attachedDatabase.typeMapping
|
endlessPlayback: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}endless_playback'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}endless_playback'])!,
|
||||||
enableConnect: attachedDatabase.typeMapping
|
playerWakelock: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}enable_connect'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}player_wakelock'])!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,7 +756,6 @@ class PreferencesTableData extends DataClass
|
|||||||
final int id;
|
final int id;
|
||||||
final SourceQualities audioQuality;
|
final SourceQualities audioQuality;
|
||||||
final bool albumColorSync;
|
final bool albumColorSync;
|
||||||
final bool amoledDarkTheme;
|
|
||||||
final bool checkUpdate;
|
final bool checkUpdate;
|
||||||
final bool normalizeAudio;
|
final bool normalizeAudio;
|
||||||
final bool showSystemTrayIcon;
|
final bool showSystemTrayIcon;
|
||||||
@ -791,18 +770,17 @@ class PreferencesTableData extends DataClass
|
|||||||
final String downloadLocation;
|
final String downloadLocation;
|
||||||
final List<String> localLibraryLocation;
|
final List<String> localLibraryLocation;
|
||||||
final String pipedInstance;
|
final String pipedInstance;
|
||||||
|
final String neteaseApiInstance;
|
||||||
final ThemeMode themeMode;
|
final ThemeMode themeMode;
|
||||||
final AudioSource audioSource;
|
final AudioSource audioSource;
|
||||||
final SourceCodecs streamMusicCodec;
|
final SourceCodecs streamMusicCodec;
|
||||||
final SourceCodecs downloadMusicCodec;
|
final SourceCodecs downloadMusicCodec;
|
||||||
final bool discordPresence;
|
|
||||||
final bool endlessPlayback;
|
final bool endlessPlayback;
|
||||||
final bool enableConnect;
|
final bool playerWakelock;
|
||||||
const PreferencesTableData(
|
const PreferencesTableData(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
required this.audioQuality,
|
required this.audioQuality,
|
||||||
required this.albumColorSync,
|
required this.albumColorSync,
|
||||||
required this.amoledDarkTheme,
|
|
||||||
required this.checkUpdate,
|
required this.checkUpdate,
|
||||||
required this.normalizeAudio,
|
required this.normalizeAudio,
|
||||||
required this.showSystemTrayIcon,
|
required this.showSystemTrayIcon,
|
||||||
@ -817,13 +795,13 @@ class PreferencesTableData extends DataClass
|
|||||||
required this.downloadLocation,
|
required this.downloadLocation,
|
||||||
required this.localLibraryLocation,
|
required this.localLibraryLocation,
|
||||||
required this.pipedInstance,
|
required this.pipedInstance,
|
||||||
|
required this.neteaseApiInstance,
|
||||||
required this.themeMode,
|
required this.themeMode,
|
||||||
required this.audioSource,
|
required this.audioSource,
|
||||||
required this.streamMusicCodec,
|
required this.streamMusicCodec,
|
||||||
required this.downloadMusicCodec,
|
required this.downloadMusicCodec,
|
||||||
required this.discordPresence,
|
|
||||||
required this.endlessPlayback,
|
required this.endlessPlayback,
|
||||||
required this.enableConnect});
|
required this.playerWakelock});
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, Expression>{};
|
final map = <String, Expression>{};
|
||||||
@ -833,7 +811,6 @@ class PreferencesTableData extends DataClass
|
|||||||
$PreferencesTableTable.$converteraudioQuality.toSql(audioQuality));
|
$PreferencesTableTable.$converteraudioQuality.toSql(audioQuality));
|
||||||
}
|
}
|
||||||
map['album_color_sync'] = Variable<bool>(albumColorSync);
|
map['album_color_sync'] = Variable<bool>(albumColorSync);
|
||||||
map['amoled_dark_theme'] = Variable<bool>(amoledDarkTheme);
|
|
||||||
map['check_update'] = Variable<bool>(checkUpdate);
|
map['check_update'] = Variable<bool>(checkUpdate);
|
||||||
map['normalize_audio'] = Variable<bool>(normalizeAudio);
|
map['normalize_audio'] = Variable<bool>(normalizeAudio);
|
||||||
map['show_system_tray_icon'] = Variable<bool>(showSystemTrayIcon);
|
map['show_system_tray_icon'] = Variable<bool>(showSystemTrayIcon);
|
||||||
@ -871,6 +848,7 @@ class PreferencesTableData extends DataClass
|
|||||||
.toSql(localLibraryLocation));
|
.toSql(localLibraryLocation));
|
||||||
}
|
}
|
||||||
map['piped_instance'] = Variable<String>(pipedInstance);
|
map['piped_instance'] = Variable<String>(pipedInstance);
|
||||||
|
map['netease_api_instance'] = Variable<String>(neteaseApiInstance);
|
||||||
{
|
{
|
||||||
map['theme_mode'] = Variable<String>(
|
map['theme_mode'] = Variable<String>(
|
||||||
$PreferencesTableTable.$converterthemeMode.toSql(themeMode));
|
$PreferencesTableTable.$converterthemeMode.toSql(themeMode));
|
||||||
@ -889,9 +867,8 @@ class PreferencesTableData extends DataClass
|
|||||||
.$converterdownloadMusicCodec
|
.$converterdownloadMusicCodec
|
||||||
.toSql(downloadMusicCodec));
|
.toSql(downloadMusicCodec));
|
||||||
}
|
}
|
||||||
map['discord_presence'] = Variable<bool>(discordPresence);
|
|
||||||
map['endless_playback'] = Variable<bool>(endlessPlayback);
|
map['endless_playback'] = Variable<bool>(endlessPlayback);
|
||||||
map['enable_connect'] = Variable<bool>(enableConnect);
|
map['player_wakelock'] = Variable<bool>(playerWakelock);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +877,6 @@ class PreferencesTableData extends DataClass
|
|||||||
id: Value(id),
|
id: Value(id),
|
||||||
audioQuality: Value(audioQuality),
|
audioQuality: Value(audioQuality),
|
||||||
albumColorSync: Value(albumColorSync),
|
albumColorSync: Value(albumColorSync),
|
||||||
amoledDarkTheme: Value(amoledDarkTheme),
|
|
||||||
checkUpdate: Value(checkUpdate),
|
checkUpdate: Value(checkUpdate),
|
||||||
normalizeAudio: Value(normalizeAudio),
|
normalizeAudio: Value(normalizeAudio),
|
||||||
showSystemTrayIcon: Value(showSystemTrayIcon),
|
showSystemTrayIcon: Value(showSystemTrayIcon),
|
||||||
@ -915,13 +891,13 @@ class PreferencesTableData extends DataClass
|
|||||||
downloadLocation: Value(downloadLocation),
|
downloadLocation: Value(downloadLocation),
|
||||||
localLibraryLocation: Value(localLibraryLocation),
|
localLibraryLocation: Value(localLibraryLocation),
|
||||||
pipedInstance: Value(pipedInstance),
|
pipedInstance: Value(pipedInstance),
|
||||||
|
neteaseApiInstance: Value(neteaseApiInstance),
|
||||||
themeMode: Value(themeMode),
|
themeMode: Value(themeMode),
|
||||||
audioSource: Value(audioSource),
|
audioSource: Value(audioSource),
|
||||||
streamMusicCodec: Value(streamMusicCodec),
|
streamMusicCodec: Value(streamMusicCodec),
|
||||||
downloadMusicCodec: Value(downloadMusicCodec),
|
downloadMusicCodec: Value(downloadMusicCodec),
|
||||||
discordPresence: Value(discordPresence),
|
|
||||||
endlessPlayback: Value(endlessPlayback),
|
endlessPlayback: Value(endlessPlayback),
|
||||||
enableConnect: Value(enableConnect),
|
playerWakelock: Value(playerWakelock),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,7 +909,6 @@ class PreferencesTableData extends DataClass
|
|||||||
audioQuality: $PreferencesTableTable.$converteraudioQuality
|
audioQuality: $PreferencesTableTable.$converteraudioQuality
|
||||||
.fromJson(serializer.fromJson<String>(json['audioQuality'])),
|
.fromJson(serializer.fromJson<String>(json['audioQuality'])),
|
||||||
albumColorSync: serializer.fromJson<bool>(json['albumColorSync']),
|
albumColorSync: serializer.fromJson<bool>(json['albumColorSync']),
|
||||||
amoledDarkTheme: serializer.fromJson<bool>(json['amoledDarkTheme']),
|
|
||||||
checkUpdate: serializer.fromJson<bool>(json['checkUpdate']),
|
checkUpdate: serializer.fromJson<bool>(json['checkUpdate']),
|
||||||
normalizeAudio: serializer.fromJson<bool>(json['normalizeAudio']),
|
normalizeAudio: serializer.fromJson<bool>(json['normalizeAudio']),
|
||||||
showSystemTrayIcon: serializer.fromJson<bool>(json['showSystemTrayIcon']),
|
showSystemTrayIcon: serializer.fromJson<bool>(json['showSystemTrayIcon']),
|
||||||
@ -954,6 +929,8 @@ class PreferencesTableData extends DataClass
|
|||||||
localLibraryLocation:
|
localLibraryLocation:
|
||||||
serializer.fromJson<List<String>>(json['localLibraryLocation']),
|
serializer.fromJson<List<String>>(json['localLibraryLocation']),
|
||||||
pipedInstance: serializer.fromJson<String>(json['pipedInstance']),
|
pipedInstance: serializer.fromJson<String>(json['pipedInstance']),
|
||||||
|
neteaseApiInstance:
|
||||||
|
serializer.fromJson<String>(json['neteaseApiInstance']),
|
||||||
themeMode: $PreferencesTableTable.$converterthemeMode
|
themeMode: $PreferencesTableTable.$converterthemeMode
|
||||||
.fromJson(serializer.fromJson<String>(json['themeMode'])),
|
.fromJson(serializer.fromJson<String>(json['themeMode'])),
|
||||||
audioSource: $PreferencesTableTable.$converteraudioSource
|
audioSource: $PreferencesTableTable.$converteraudioSource
|
||||||
@ -962,9 +939,8 @@ class PreferencesTableData extends DataClass
|
|||||||
.fromJson(serializer.fromJson<String>(json['streamMusicCodec'])),
|
.fromJson(serializer.fromJson<String>(json['streamMusicCodec'])),
|
||||||
downloadMusicCodec: $PreferencesTableTable.$converterdownloadMusicCodec
|
downloadMusicCodec: $PreferencesTableTable.$converterdownloadMusicCodec
|
||||||
.fromJson(serializer.fromJson<String>(json['downloadMusicCodec'])),
|
.fromJson(serializer.fromJson<String>(json['downloadMusicCodec'])),
|
||||||
discordPresence: serializer.fromJson<bool>(json['discordPresence']),
|
|
||||||
endlessPlayback: serializer.fromJson<bool>(json['endlessPlayback']),
|
endlessPlayback: serializer.fromJson<bool>(json['endlessPlayback']),
|
||||||
enableConnect: serializer.fromJson<bool>(json['enableConnect']),
|
playerWakelock: serializer.fromJson<bool>(json['playerWakelock']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
@ -975,7 +951,6 @@ class PreferencesTableData extends DataClass
|
|||||||
'audioQuality': serializer.toJson<String>(
|
'audioQuality': serializer.toJson<String>(
|
||||||
$PreferencesTableTable.$converteraudioQuality.toJson(audioQuality)),
|
$PreferencesTableTable.$converteraudioQuality.toJson(audioQuality)),
|
||||||
'albumColorSync': serializer.toJson<bool>(albumColorSync),
|
'albumColorSync': serializer.toJson<bool>(albumColorSync),
|
||||||
'amoledDarkTheme': serializer.toJson<bool>(amoledDarkTheme),
|
|
||||||
'checkUpdate': serializer.toJson<bool>(checkUpdate),
|
'checkUpdate': serializer.toJson<bool>(checkUpdate),
|
||||||
'normalizeAudio': serializer.toJson<bool>(normalizeAudio),
|
'normalizeAudio': serializer.toJson<bool>(normalizeAudio),
|
||||||
'showSystemTrayIcon': serializer.toJson<bool>(showSystemTrayIcon),
|
'showSystemTrayIcon': serializer.toJson<bool>(showSystemTrayIcon),
|
||||||
@ -995,6 +970,7 @@ class PreferencesTableData extends DataClass
|
|||||||
'localLibraryLocation':
|
'localLibraryLocation':
|
||||||
serializer.toJson<List<String>>(localLibraryLocation),
|
serializer.toJson<List<String>>(localLibraryLocation),
|
||||||
'pipedInstance': serializer.toJson<String>(pipedInstance),
|
'pipedInstance': serializer.toJson<String>(pipedInstance),
|
||||||
|
'neteaseApiInstance': serializer.toJson<String>(neteaseApiInstance),
|
||||||
'themeMode': serializer.toJson<String>(
|
'themeMode': serializer.toJson<String>(
|
||||||
$PreferencesTableTable.$converterthemeMode.toJson(themeMode)),
|
$PreferencesTableTable.$converterthemeMode.toJson(themeMode)),
|
||||||
'audioSource': serializer.toJson<String>(
|
'audioSource': serializer.toJson<String>(
|
||||||
@ -1005,9 +981,8 @@ class PreferencesTableData extends DataClass
|
|||||||
'downloadMusicCodec': serializer.toJson<String>($PreferencesTableTable
|
'downloadMusicCodec': serializer.toJson<String>($PreferencesTableTable
|
||||||
.$converterdownloadMusicCodec
|
.$converterdownloadMusicCodec
|
||||||
.toJson(downloadMusicCodec)),
|
.toJson(downloadMusicCodec)),
|
||||||
'discordPresence': serializer.toJson<bool>(discordPresence),
|
|
||||||
'endlessPlayback': serializer.toJson<bool>(endlessPlayback),
|
'endlessPlayback': serializer.toJson<bool>(endlessPlayback),
|
||||||
'enableConnect': serializer.toJson<bool>(enableConnect),
|
'playerWakelock': serializer.toJson<bool>(playerWakelock),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,7 +990,6 @@ class PreferencesTableData extends DataClass
|
|||||||
{int? id,
|
{int? id,
|
||||||
SourceQualities? audioQuality,
|
SourceQualities? audioQuality,
|
||||||
bool? albumColorSync,
|
bool? albumColorSync,
|
||||||
bool? amoledDarkTheme,
|
|
||||||
bool? checkUpdate,
|
bool? checkUpdate,
|
||||||
bool? normalizeAudio,
|
bool? normalizeAudio,
|
||||||
bool? showSystemTrayIcon,
|
bool? showSystemTrayIcon,
|
||||||
@ -1030,18 +1004,17 @@ class PreferencesTableData extends DataClass
|
|||||||
String? downloadLocation,
|
String? downloadLocation,
|
||||||
List<String>? localLibraryLocation,
|
List<String>? localLibraryLocation,
|
||||||
String? pipedInstance,
|
String? pipedInstance,
|
||||||
|
String? neteaseApiInstance,
|
||||||
ThemeMode? themeMode,
|
ThemeMode? themeMode,
|
||||||
AudioSource? audioSource,
|
AudioSource? audioSource,
|
||||||
SourceCodecs? streamMusicCodec,
|
SourceCodecs? streamMusicCodec,
|
||||||
SourceCodecs? downloadMusicCodec,
|
SourceCodecs? downloadMusicCodec,
|
||||||
bool? discordPresence,
|
|
||||||
bool? endlessPlayback,
|
bool? endlessPlayback,
|
||||||
bool? enableConnect}) =>
|
bool? playerWakelock}) =>
|
||||||
PreferencesTableData(
|
PreferencesTableData(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
audioQuality: audioQuality ?? this.audioQuality,
|
audioQuality: audioQuality ?? this.audioQuality,
|
||||||
albumColorSync: albumColorSync ?? this.albumColorSync,
|
albumColorSync: albumColorSync ?? this.albumColorSync,
|
||||||
amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme,
|
|
||||||
checkUpdate: checkUpdate ?? this.checkUpdate,
|
checkUpdate: checkUpdate ?? this.checkUpdate,
|
||||||
normalizeAudio: normalizeAudio ?? this.normalizeAudio,
|
normalizeAudio: normalizeAudio ?? this.normalizeAudio,
|
||||||
showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon,
|
showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon,
|
||||||
@ -1056,13 +1029,13 @@ class PreferencesTableData extends DataClass
|
|||||||
downloadLocation: downloadLocation ?? this.downloadLocation,
|
downloadLocation: downloadLocation ?? this.downloadLocation,
|
||||||
localLibraryLocation: localLibraryLocation ?? this.localLibraryLocation,
|
localLibraryLocation: localLibraryLocation ?? this.localLibraryLocation,
|
||||||
pipedInstance: pipedInstance ?? this.pipedInstance,
|
pipedInstance: pipedInstance ?? this.pipedInstance,
|
||||||
|
neteaseApiInstance: neteaseApiInstance ?? this.neteaseApiInstance,
|
||||||
themeMode: themeMode ?? this.themeMode,
|
themeMode: themeMode ?? this.themeMode,
|
||||||
audioSource: audioSource ?? this.audioSource,
|
audioSource: audioSource ?? this.audioSource,
|
||||||
streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec,
|
streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec,
|
||||||
downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec,
|
downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec,
|
||||||
discordPresence: discordPresence ?? this.discordPresence,
|
|
||||||
endlessPlayback: endlessPlayback ?? this.endlessPlayback,
|
endlessPlayback: endlessPlayback ?? this.endlessPlayback,
|
||||||
enableConnect: enableConnect ?? this.enableConnect,
|
playerWakelock: playerWakelock ?? this.playerWakelock,
|
||||||
);
|
);
|
||||||
PreferencesTableData copyWithCompanion(PreferencesTableCompanion data) {
|
PreferencesTableData copyWithCompanion(PreferencesTableCompanion data) {
|
||||||
return PreferencesTableData(
|
return PreferencesTableData(
|
||||||
@ -1073,9 +1046,6 @@ class PreferencesTableData extends DataClass
|
|||||||
albumColorSync: data.albumColorSync.present
|
albumColorSync: data.albumColorSync.present
|
||||||
? data.albumColorSync.value
|
? data.albumColorSync.value
|
||||||
: this.albumColorSync,
|
: this.albumColorSync,
|
||||||
amoledDarkTheme: data.amoledDarkTheme.present
|
|
||||||
? data.amoledDarkTheme.value
|
|
||||||
: this.amoledDarkTheme,
|
|
||||||
checkUpdate:
|
checkUpdate:
|
||||||
data.checkUpdate.present ? data.checkUpdate.value : this.checkUpdate,
|
data.checkUpdate.present ? data.checkUpdate.value : this.checkUpdate,
|
||||||
normalizeAudio: data.normalizeAudio.present
|
normalizeAudio: data.normalizeAudio.present
|
||||||
@ -1111,6 +1081,9 @@ class PreferencesTableData extends DataClass
|
|||||||
pipedInstance: data.pipedInstance.present
|
pipedInstance: data.pipedInstance.present
|
||||||
? data.pipedInstance.value
|
? data.pipedInstance.value
|
||||||
: this.pipedInstance,
|
: this.pipedInstance,
|
||||||
|
neteaseApiInstance: data.neteaseApiInstance.present
|
||||||
|
? data.neteaseApiInstance.value
|
||||||
|
: this.neteaseApiInstance,
|
||||||
themeMode: data.themeMode.present ? data.themeMode.value : this.themeMode,
|
themeMode: data.themeMode.present ? data.themeMode.value : this.themeMode,
|
||||||
audioSource:
|
audioSource:
|
||||||
data.audioSource.present ? data.audioSource.value : this.audioSource,
|
data.audioSource.present ? data.audioSource.value : this.audioSource,
|
||||||
@ -1120,15 +1093,12 @@ class PreferencesTableData extends DataClass
|
|||||||
downloadMusicCodec: data.downloadMusicCodec.present
|
downloadMusicCodec: data.downloadMusicCodec.present
|
||||||
? data.downloadMusicCodec.value
|
? data.downloadMusicCodec.value
|
||||||
: this.downloadMusicCodec,
|
: this.downloadMusicCodec,
|
||||||
discordPresence: data.discordPresence.present
|
|
||||||
? data.discordPresence.value
|
|
||||||
: this.discordPresence,
|
|
||||||
endlessPlayback: data.endlessPlayback.present
|
endlessPlayback: data.endlessPlayback.present
|
||||||
? data.endlessPlayback.value
|
? data.endlessPlayback.value
|
||||||
: this.endlessPlayback,
|
: this.endlessPlayback,
|
||||||
enableConnect: data.enableConnect.present
|
playerWakelock: data.playerWakelock.present
|
||||||
? data.enableConnect.value
|
? data.playerWakelock.value
|
||||||
: this.enableConnect,
|
: this.playerWakelock,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1138,7 +1108,6 @@ class PreferencesTableData extends DataClass
|
|||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('audioQuality: $audioQuality, ')
|
..write('audioQuality: $audioQuality, ')
|
||||||
..write('albumColorSync: $albumColorSync, ')
|
..write('albumColorSync: $albumColorSync, ')
|
||||||
..write('amoledDarkTheme: $amoledDarkTheme, ')
|
|
||||||
..write('checkUpdate: $checkUpdate, ')
|
..write('checkUpdate: $checkUpdate, ')
|
||||||
..write('normalizeAudio: $normalizeAudio, ')
|
..write('normalizeAudio: $normalizeAudio, ')
|
||||||
..write('showSystemTrayIcon: $showSystemTrayIcon, ')
|
..write('showSystemTrayIcon: $showSystemTrayIcon, ')
|
||||||
@ -1153,13 +1122,13 @@ class PreferencesTableData extends DataClass
|
|||||||
..write('downloadLocation: $downloadLocation, ')
|
..write('downloadLocation: $downloadLocation, ')
|
||||||
..write('localLibraryLocation: $localLibraryLocation, ')
|
..write('localLibraryLocation: $localLibraryLocation, ')
|
||||||
..write('pipedInstance: $pipedInstance, ')
|
..write('pipedInstance: $pipedInstance, ')
|
||||||
|
..write('neteaseApiInstance: $neteaseApiInstance, ')
|
||||||
..write('themeMode: $themeMode, ')
|
..write('themeMode: $themeMode, ')
|
||||||
..write('audioSource: $audioSource, ')
|
..write('audioSource: $audioSource, ')
|
||||||
..write('streamMusicCodec: $streamMusicCodec, ')
|
..write('streamMusicCodec: $streamMusicCodec, ')
|
||||||
..write('downloadMusicCodec: $downloadMusicCodec, ')
|
..write('downloadMusicCodec: $downloadMusicCodec, ')
|
||||||
..write('discordPresence: $discordPresence, ')
|
|
||||||
..write('endlessPlayback: $endlessPlayback, ')
|
..write('endlessPlayback: $endlessPlayback, ')
|
||||||
..write('enableConnect: $enableConnect')
|
..write('playerWakelock: $playerWakelock')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
@ -1169,7 +1138,6 @@ class PreferencesTableData extends DataClass
|
|||||||
id,
|
id,
|
||||||
audioQuality,
|
audioQuality,
|
||||||
albumColorSync,
|
albumColorSync,
|
||||||
amoledDarkTheme,
|
|
||||||
checkUpdate,
|
checkUpdate,
|
||||||
normalizeAudio,
|
normalizeAudio,
|
||||||
showSystemTrayIcon,
|
showSystemTrayIcon,
|
||||||
@ -1184,13 +1152,13 @@ class PreferencesTableData extends DataClass
|
|||||||
downloadLocation,
|
downloadLocation,
|
||||||
localLibraryLocation,
|
localLibraryLocation,
|
||||||
pipedInstance,
|
pipedInstance,
|
||||||
|
neteaseApiInstance,
|
||||||
themeMode,
|
themeMode,
|
||||||
audioSource,
|
audioSource,
|
||||||
streamMusicCodec,
|
streamMusicCodec,
|
||||||
downloadMusicCodec,
|
downloadMusicCodec,
|
||||||
discordPresence,
|
|
||||||
endlessPlayback,
|
endlessPlayback,
|
||||||
enableConnect
|
playerWakelock
|
||||||
]);
|
]);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
@ -1199,7 +1167,6 @@ class PreferencesTableData extends DataClass
|
|||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
other.audioQuality == this.audioQuality &&
|
other.audioQuality == this.audioQuality &&
|
||||||
other.albumColorSync == this.albumColorSync &&
|
other.albumColorSync == this.albumColorSync &&
|
||||||
other.amoledDarkTheme == this.amoledDarkTheme &&
|
|
||||||
other.checkUpdate == this.checkUpdate &&
|
other.checkUpdate == this.checkUpdate &&
|
||||||
other.normalizeAudio == this.normalizeAudio &&
|
other.normalizeAudio == this.normalizeAudio &&
|
||||||
other.showSystemTrayIcon == this.showSystemTrayIcon &&
|
other.showSystemTrayIcon == this.showSystemTrayIcon &&
|
||||||
@ -1214,20 +1181,19 @@ class PreferencesTableData extends DataClass
|
|||||||
other.downloadLocation == this.downloadLocation &&
|
other.downloadLocation == this.downloadLocation &&
|
||||||
other.localLibraryLocation == this.localLibraryLocation &&
|
other.localLibraryLocation == this.localLibraryLocation &&
|
||||||
other.pipedInstance == this.pipedInstance &&
|
other.pipedInstance == this.pipedInstance &&
|
||||||
|
other.neteaseApiInstance == this.neteaseApiInstance &&
|
||||||
other.themeMode == this.themeMode &&
|
other.themeMode == this.themeMode &&
|
||||||
other.audioSource == this.audioSource &&
|
other.audioSource == this.audioSource &&
|
||||||
other.streamMusicCodec == this.streamMusicCodec &&
|
other.streamMusicCodec == this.streamMusicCodec &&
|
||||||
other.downloadMusicCodec == this.downloadMusicCodec &&
|
other.downloadMusicCodec == this.downloadMusicCodec &&
|
||||||
other.discordPresence == this.discordPresence &&
|
|
||||||
other.endlessPlayback == this.endlessPlayback &&
|
other.endlessPlayback == this.endlessPlayback &&
|
||||||
other.enableConnect == this.enableConnect);
|
other.playerWakelock == this.playerWakelock);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
||||||
final Value<int> id;
|
final Value<int> id;
|
||||||
final Value<SourceQualities> audioQuality;
|
final Value<SourceQualities> audioQuality;
|
||||||
final Value<bool> albumColorSync;
|
final Value<bool> albumColorSync;
|
||||||
final Value<bool> amoledDarkTheme;
|
|
||||||
final Value<bool> checkUpdate;
|
final Value<bool> checkUpdate;
|
||||||
final Value<bool> normalizeAudio;
|
final Value<bool> normalizeAudio;
|
||||||
final Value<bool> showSystemTrayIcon;
|
final Value<bool> showSystemTrayIcon;
|
||||||
@ -1242,18 +1208,17 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
final Value<String> downloadLocation;
|
final Value<String> downloadLocation;
|
||||||
final Value<List<String>> localLibraryLocation;
|
final Value<List<String>> localLibraryLocation;
|
||||||
final Value<String> pipedInstance;
|
final Value<String> pipedInstance;
|
||||||
|
final Value<String> neteaseApiInstance;
|
||||||
final Value<ThemeMode> themeMode;
|
final Value<ThemeMode> themeMode;
|
||||||
final Value<AudioSource> audioSource;
|
final Value<AudioSource> audioSource;
|
||||||
final Value<SourceCodecs> streamMusicCodec;
|
final Value<SourceCodecs> streamMusicCodec;
|
||||||
final Value<SourceCodecs> downloadMusicCodec;
|
final Value<SourceCodecs> downloadMusicCodec;
|
||||||
final Value<bool> discordPresence;
|
|
||||||
final Value<bool> endlessPlayback;
|
final Value<bool> endlessPlayback;
|
||||||
final Value<bool> enableConnect;
|
final Value<bool> playerWakelock;
|
||||||
const PreferencesTableCompanion({
|
const PreferencesTableCompanion({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
this.audioQuality = const Value.absent(),
|
this.audioQuality = const Value.absent(),
|
||||||
this.albumColorSync = const Value.absent(),
|
this.albumColorSync = const Value.absent(),
|
||||||
this.amoledDarkTheme = const Value.absent(),
|
|
||||||
this.checkUpdate = const Value.absent(),
|
this.checkUpdate = const Value.absent(),
|
||||||
this.normalizeAudio = const Value.absent(),
|
this.normalizeAudio = const Value.absent(),
|
||||||
this.showSystemTrayIcon = const Value.absent(),
|
this.showSystemTrayIcon = const Value.absent(),
|
||||||
@ -1268,19 +1233,18 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
this.downloadLocation = const Value.absent(),
|
this.downloadLocation = const Value.absent(),
|
||||||
this.localLibraryLocation = const Value.absent(),
|
this.localLibraryLocation = const Value.absent(),
|
||||||
this.pipedInstance = const Value.absent(),
|
this.pipedInstance = const Value.absent(),
|
||||||
|
this.neteaseApiInstance = const Value.absent(),
|
||||||
this.themeMode = const Value.absent(),
|
this.themeMode = const Value.absent(),
|
||||||
this.audioSource = const Value.absent(),
|
this.audioSource = const Value.absent(),
|
||||||
this.streamMusicCodec = const Value.absent(),
|
this.streamMusicCodec = const Value.absent(),
|
||||||
this.downloadMusicCodec = const Value.absent(),
|
this.downloadMusicCodec = const Value.absent(),
|
||||||
this.discordPresence = const Value.absent(),
|
|
||||||
this.endlessPlayback = const Value.absent(),
|
this.endlessPlayback = const Value.absent(),
|
||||||
this.enableConnect = const Value.absent(),
|
this.playerWakelock = const Value.absent(),
|
||||||
});
|
});
|
||||||
PreferencesTableCompanion.insert({
|
PreferencesTableCompanion.insert({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
this.audioQuality = const Value.absent(),
|
this.audioQuality = const Value.absent(),
|
||||||
this.albumColorSync = const Value.absent(),
|
this.albumColorSync = const Value.absent(),
|
||||||
this.amoledDarkTheme = const Value.absent(),
|
|
||||||
this.checkUpdate = const Value.absent(),
|
this.checkUpdate = const Value.absent(),
|
||||||
this.normalizeAudio = const Value.absent(),
|
this.normalizeAudio = const Value.absent(),
|
||||||
this.showSystemTrayIcon = const Value.absent(),
|
this.showSystemTrayIcon = const Value.absent(),
|
||||||
@ -1295,19 +1259,18 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
this.downloadLocation = const Value.absent(),
|
this.downloadLocation = const Value.absent(),
|
||||||
this.localLibraryLocation = const Value.absent(),
|
this.localLibraryLocation = const Value.absent(),
|
||||||
this.pipedInstance = const Value.absent(),
|
this.pipedInstance = const Value.absent(),
|
||||||
|
this.neteaseApiInstance = const Value.absent(),
|
||||||
this.themeMode = const Value.absent(),
|
this.themeMode = const Value.absent(),
|
||||||
this.audioSource = const Value.absent(),
|
this.audioSource = const Value.absent(),
|
||||||
this.streamMusicCodec = const Value.absent(),
|
this.streamMusicCodec = const Value.absent(),
|
||||||
this.downloadMusicCodec = const Value.absent(),
|
this.downloadMusicCodec = const Value.absent(),
|
||||||
this.discordPresence = const Value.absent(),
|
|
||||||
this.endlessPlayback = const Value.absent(),
|
this.endlessPlayback = const Value.absent(),
|
||||||
this.enableConnect = const Value.absent(),
|
this.playerWakelock = const Value.absent(),
|
||||||
});
|
});
|
||||||
static Insertable<PreferencesTableData> custom({
|
static Insertable<PreferencesTableData> custom({
|
||||||
Expression<int>? id,
|
Expression<int>? id,
|
||||||
Expression<String>? audioQuality,
|
Expression<String>? audioQuality,
|
||||||
Expression<bool>? albumColorSync,
|
Expression<bool>? albumColorSync,
|
||||||
Expression<bool>? amoledDarkTheme,
|
|
||||||
Expression<bool>? checkUpdate,
|
Expression<bool>? checkUpdate,
|
||||||
Expression<bool>? normalizeAudio,
|
Expression<bool>? normalizeAudio,
|
||||||
Expression<bool>? showSystemTrayIcon,
|
Expression<bool>? showSystemTrayIcon,
|
||||||
@ -1322,19 +1285,18 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
Expression<String>? downloadLocation,
|
Expression<String>? downloadLocation,
|
||||||
Expression<String>? localLibraryLocation,
|
Expression<String>? localLibraryLocation,
|
||||||
Expression<String>? pipedInstance,
|
Expression<String>? pipedInstance,
|
||||||
|
Expression<String>? neteaseApiInstance,
|
||||||
Expression<String>? themeMode,
|
Expression<String>? themeMode,
|
||||||
Expression<String>? audioSource,
|
Expression<String>? audioSource,
|
||||||
Expression<String>? streamMusicCodec,
|
Expression<String>? streamMusicCodec,
|
||||||
Expression<String>? downloadMusicCodec,
|
Expression<String>? downloadMusicCodec,
|
||||||
Expression<bool>? discordPresence,
|
|
||||||
Expression<bool>? endlessPlayback,
|
Expression<bool>? endlessPlayback,
|
||||||
Expression<bool>? enableConnect,
|
Expression<bool>? playerWakelock,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
if (audioQuality != null) 'audio_quality': audioQuality,
|
if (audioQuality != null) 'audio_quality': audioQuality,
|
||||||
if (albumColorSync != null) 'album_color_sync': albumColorSync,
|
if (albumColorSync != null) 'album_color_sync': albumColorSync,
|
||||||
if (amoledDarkTheme != null) 'amoled_dark_theme': amoledDarkTheme,
|
|
||||||
if (checkUpdate != null) 'check_update': checkUpdate,
|
if (checkUpdate != null) 'check_update': checkUpdate,
|
||||||
if (normalizeAudio != null) 'normalize_audio': normalizeAudio,
|
if (normalizeAudio != null) 'normalize_audio': normalizeAudio,
|
||||||
if (showSystemTrayIcon != null)
|
if (showSystemTrayIcon != null)
|
||||||
@ -1351,14 +1313,15 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
if (localLibraryLocation != null)
|
if (localLibraryLocation != null)
|
||||||
'local_library_location': localLibraryLocation,
|
'local_library_location': localLibraryLocation,
|
||||||
if (pipedInstance != null) 'piped_instance': pipedInstance,
|
if (pipedInstance != null) 'piped_instance': pipedInstance,
|
||||||
|
if (neteaseApiInstance != null)
|
||||||
|
'netease_api_instance': neteaseApiInstance,
|
||||||
if (themeMode != null) 'theme_mode': themeMode,
|
if (themeMode != null) 'theme_mode': themeMode,
|
||||||
if (audioSource != null) 'audio_source': audioSource,
|
if (audioSource != null) 'audio_source': audioSource,
|
||||||
if (streamMusicCodec != null) 'stream_music_codec': streamMusicCodec,
|
if (streamMusicCodec != null) 'stream_music_codec': streamMusicCodec,
|
||||||
if (downloadMusicCodec != null)
|
if (downloadMusicCodec != null)
|
||||||
'download_music_codec': downloadMusicCodec,
|
'download_music_codec': downloadMusicCodec,
|
||||||
if (discordPresence != null) 'discord_presence': discordPresence,
|
|
||||||
if (endlessPlayback != null) 'endless_playback': endlessPlayback,
|
if (endlessPlayback != null) 'endless_playback': endlessPlayback,
|
||||||
if (enableConnect != null) 'enable_connect': enableConnect,
|
if (playerWakelock != null) 'player_wakelock': playerWakelock,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1366,7 +1329,6 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
{Value<int>? id,
|
{Value<int>? id,
|
||||||
Value<SourceQualities>? audioQuality,
|
Value<SourceQualities>? audioQuality,
|
||||||
Value<bool>? albumColorSync,
|
Value<bool>? albumColorSync,
|
||||||
Value<bool>? amoledDarkTheme,
|
|
||||||
Value<bool>? checkUpdate,
|
Value<bool>? checkUpdate,
|
||||||
Value<bool>? normalizeAudio,
|
Value<bool>? normalizeAudio,
|
||||||
Value<bool>? showSystemTrayIcon,
|
Value<bool>? showSystemTrayIcon,
|
||||||
@ -1381,18 +1343,17 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
Value<String>? downloadLocation,
|
Value<String>? downloadLocation,
|
||||||
Value<List<String>>? localLibraryLocation,
|
Value<List<String>>? localLibraryLocation,
|
||||||
Value<String>? pipedInstance,
|
Value<String>? pipedInstance,
|
||||||
|
Value<String>? neteaseApiInstance,
|
||||||
Value<ThemeMode>? themeMode,
|
Value<ThemeMode>? themeMode,
|
||||||
Value<AudioSource>? audioSource,
|
Value<AudioSource>? audioSource,
|
||||||
Value<SourceCodecs>? streamMusicCodec,
|
Value<SourceCodecs>? streamMusicCodec,
|
||||||
Value<SourceCodecs>? downloadMusicCodec,
|
Value<SourceCodecs>? downloadMusicCodec,
|
||||||
Value<bool>? discordPresence,
|
|
||||||
Value<bool>? endlessPlayback,
|
Value<bool>? endlessPlayback,
|
||||||
Value<bool>? enableConnect}) {
|
Value<bool>? playerWakelock}) {
|
||||||
return PreferencesTableCompanion(
|
return PreferencesTableCompanion(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
audioQuality: audioQuality ?? this.audioQuality,
|
audioQuality: audioQuality ?? this.audioQuality,
|
||||||
albumColorSync: albumColorSync ?? this.albumColorSync,
|
albumColorSync: albumColorSync ?? this.albumColorSync,
|
||||||
amoledDarkTheme: amoledDarkTheme ?? this.amoledDarkTheme,
|
|
||||||
checkUpdate: checkUpdate ?? this.checkUpdate,
|
checkUpdate: checkUpdate ?? this.checkUpdate,
|
||||||
normalizeAudio: normalizeAudio ?? this.normalizeAudio,
|
normalizeAudio: normalizeAudio ?? this.normalizeAudio,
|
||||||
showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon,
|
showSystemTrayIcon: showSystemTrayIcon ?? this.showSystemTrayIcon,
|
||||||
@ -1407,13 +1368,13 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
downloadLocation: downloadLocation ?? this.downloadLocation,
|
downloadLocation: downloadLocation ?? this.downloadLocation,
|
||||||
localLibraryLocation: localLibraryLocation ?? this.localLibraryLocation,
|
localLibraryLocation: localLibraryLocation ?? this.localLibraryLocation,
|
||||||
pipedInstance: pipedInstance ?? this.pipedInstance,
|
pipedInstance: pipedInstance ?? this.pipedInstance,
|
||||||
|
neteaseApiInstance: neteaseApiInstance ?? this.neteaseApiInstance,
|
||||||
themeMode: themeMode ?? this.themeMode,
|
themeMode: themeMode ?? this.themeMode,
|
||||||
audioSource: audioSource ?? this.audioSource,
|
audioSource: audioSource ?? this.audioSource,
|
||||||
streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec,
|
streamMusicCodec: streamMusicCodec ?? this.streamMusicCodec,
|
||||||
downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec,
|
downloadMusicCodec: downloadMusicCodec ?? this.downloadMusicCodec,
|
||||||
discordPresence: discordPresence ?? this.discordPresence,
|
|
||||||
endlessPlayback: endlessPlayback ?? this.endlessPlayback,
|
endlessPlayback: endlessPlayback ?? this.endlessPlayback,
|
||||||
enableConnect: enableConnect ?? this.enableConnect,
|
playerWakelock: playerWakelock ?? this.playerWakelock,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1431,9 +1392,6 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
if (albumColorSync.present) {
|
if (albumColorSync.present) {
|
||||||
map['album_color_sync'] = Variable<bool>(albumColorSync.value);
|
map['album_color_sync'] = Variable<bool>(albumColorSync.value);
|
||||||
}
|
}
|
||||||
if (amoledDarkTheme.present) {
|
|
||||||
map['amoled_dark_theme'] = Variable<bool>(amoledDarkTheme.value);
|
|
||||||
}
|
|
||||||
if (checkUpdate.present) {
|
if (checkUpdate.present) {
|
||||||
map['check_update'] = Variable<bool>(checkUpdate.value);
|
map['check_update'] = Variable<bool>(checkUpdate.value);
|
||||||
}
|
}
|
||||||
@ -1486,6 +1444,9 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
if (pipedInstance.present) {
|
if (pipedInstance.present) {
|
||||||
map['piped_instance'] = Variable<String>(pipedInstance.value);
|
map['piped_instance'] = Variable<String>(pipedInstance.value);
|
||||||
}
|
}
|
||||||
|
if (neteaseApiInstance.present) {
|
||||||
|
map['netease_api_instance'] = Variable<String>(neteaseApiInstance.value);
|
||||||
|
}
|
||||||
if (themeMode.present) {
|
if (themeMode.present) {
|
||||||
map['theme_mode'] = Variable<String>(
|
map['theme_mode'] = Variable<String>(
|
||||||
$PreferencesTableTable.$converterthemeMode.toSql(themeMode.value));
|
$PreferencesTableTable.$converterthemeMode.toSql(themeMode.value));
|
||||||
@ -1505,14 +1466,11 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
.$converterdownloadMusicCodec
|
.$converterdownloadMusicCodec
|
||||||
.toSql(downloadMusicCodec.value));
|
.toSql(downloadMusicCodec.value));
|
||||||
}
|
}
|
||||||
if (discordPresence.present) {
|
|
||||||
map['discord_presence'] = Variable<bool>(discordPresence.value);
|
|
||||||
}
|
|
||||||
if (endlessPlayback.present) {
|
if (endlessPlayback.present) {
|
||||||
map['endless_playback'] = Variable<bool>(endlessPlayback.value);
|
map['endless_playback'] = Variable<bool>(endlessPlayback.value);
|
||||||
}
|
}
|
||||||
if (enableConnect.present) {
|
if (playerWakelock.present) {
|
||||||
map['enable_connect'] = Variable<bool>(enableConnect.value);
|
map['player_wakelock'] = Variable<bool>(playerWakelock.value);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@ -1523,7 +1481,6 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('audioQuality: $audioQuality, ')
|
..write('audioQuality: $audioQuality, ')
|
||||||
..write('albumColorSync: $albumColorSync, ')
|
..write('albumColorSync: $albumColorSync, ')
|
||||||
..write('amoledDarkTheme: $amoledDarkTheme, ')
|
|
||||||
..write('checkUpdate: $checkUpdate, ')
|
..write('checkUpdate: $checkUpdate, ')
|
||||||
..write('normalizeAudio: $normalizeAudio, ')
|
..write('normalizeAudio: $normalizeAudio, ')
|
||||||
..write('showSystemTrayIcon: $showSystemTrayIcon, ')
|
..write('showSystemTrayIcon: $showSystemTrayIcon, ')
|
||||||
@ -1538,13 +1495,13 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
|
|||||||
..write('downloadLocation: $downloadLocation, ')
|
..write('downloadLocation: $downloadLocation, ')
|
||||||
..write('localLibraryLocation: $localLibraryLocation, ')
|
..write('localLibraryLocation: $localLibraryLocation, ')
|
||||||
..write('pipedInstance: $pipedInstance, ')
|
..write('pipedInstance: $pipedInstance, ')
|
||||||
|
..write('neteaseApiInstance: $neteaseApiInstance, ')
|
||||||
..write('themeMode: $themeMode, ')
|
..write('themeMode: $themeMode, ')
|
||||||
..write('audioSource: $audioSource, ')
|
..write('audioSource: $audioSource, ')
|
||||||
..write('streamMusicCodec: $streamMusicCodec, ')
|
..write('streamMusicCodec: $streamMusicCodec, ')
|
||||||
..write('downloadMusicCodec: $downloadMusicCodec, ')
|
..write('downloadMusicCodec: $downloadMusicCodec, ')
|
||||||
..write('discordPresence: $discordPresence, ')
|
|
||||||
..write('endlessPlayback: $endlessPlayback, ')
|
..write('endlessPlayback: $endlessPlayback, ')
|
||||||
..write('enableConnect: $enableConnect')
|
..write('playerWakelock: $playerWakelock')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
@ -3980,7 +3937,6 @@ typedef $$PreferencesTableTableCreateCompanionBuilder
|
|||||||
Value<int> id,
|
Value<int> id,
|
||||||
Value<SourceQualities> audioQuality,
|
Value<SourceQualities> audioQuality,
|
||||||
Value<bool> albumColorSync,
|
Value<bool> albumColorSync,
|
||||||
Value<bool> amoledDarkTheme,
|
|
||||||
Value<bool> checkUpdate,
|
Value<bool> checkUpdate,
|
||||||
Value<bool> normalizeAudio,
|
Value<bool> normalizeAudio,
|
||||||
Value<bool> showSystemTrayIcon,
|
Value<bool> showSystemTrayIcon,
|
||||||
@ -3995,20 +3951,19 @@ typedef $$PreferencesTableTableCreateCompanionBuilder
|
|||||||
Value<String> downloadLocation,
|
Value<String> downloadLocation,
|
||||||
Value<List<String>> localLibraryLocation,
|
Value<List<String>> localLibraryLocation,
|
||||||
Value<String> pipedInstance,
|
Value<String> pipedInstance,
|
||||||
|
Value<String> neteaseApiInstance,
|
||||||
Value<ThemeMode> themeMode,
|
Value<ThemeMode> themeMode,
|
||||||
Value<AudioSource> audioSource,
|
Value<AudioSource> audioSource,
|
||||||
Value<SourceCodecs> streamMusicCodec,
|
Value<SourceCodecs> streamMusicCodec,
|
||||||
Value<SourceCodecs> downloadMusicCodec,
|
Value<SourceCodecs> downloadMusicCodec,
|
||||||
Value<bool> discordPresence,
|
|
||||||
Value<bool> endlessPlayback,
|
Value<bool> endlessPlayback,
|
||||||
Value<bool> enableConnect,
|
Value<bool> playerWakelock,
|
||||||
});
|
});
|
||||||
typedef $$PreferencesTableTableUpdateCompanionBuilder
|
typedef $$PreferencesTableTableUpdateCompanionBuilder
|
||||||
= PreferencesTableCompanion Function({
|
= PreferencesTableCompanion Function({
|
||||||
Value<int> id,
|
Value<int> id,
|
||||||
Value<SourceQualities> audioQuality,
|
Value<SourceQualities> audioQuality,
|
||||||
Value<bool> albumColorSync,
|
Value<bool> albumColorSync,
|
||||||
Value<bool> amoledDarkTheme,
|
|
||||||
Value<bool> checkUpdate,
|
Value<bool> checkUpdate,
|
||||||
Value<bool> normalizeAudio,
|
Value<bool> normalizeAudio,
|
||||||
Value<bool> showSystemTrayIcon,
|
Value<bool> showSystemTrayIcon,
|
||||||
@ -4023,13 +3978,13 @@ typedef $$PreferencesTableTableUpdateCompanionBuilder
|
|||||||
Value<String> downloadLocation,
|
Value<String> downloadLocation,
|
||||||
Value<List<String>> localLibraryLocation,
|
Value<List<String>> localLibraryLocation,
|
||||||
Value<String> pipedInstance,
|
Value<String> pipedInstance,
|
||||||
|
Value<String> neteaseApiInstance,
|
||||||
Value<ThemeMode> themeMode,
|
Value<ThemeMode> themeMode,
|
||||||
Value<AudioSource> audioSource,
|
Value<AudioSource> audioSource,
|
||||||
Value<SourceCodecs> streamMusicCodec,
|
Value<SourceCodecs> streamMusicCodec,
|
||||||
Value<SourceCodecs> downloadMusicCodec,
|
Value<SourceCodecs> downloadMusicCodec,
|
||||||
Value<bool> discordPresence,
|
|
||||||
Value<bool> endlessPlayback,
|
Value<bool> endlessPlayback,
|
||||||
Value<bool> enableConnect,
|
Value<bool> playerWakelock,
|
||||||
});
|
});
|
||||||
|
|
||||||
class $$PreferencesTableTableTableManager extends RootTableManager<
|
class $$PreferencesTableTableTableManager extends RootTableManager<
|
||||||
@ -4053,7 +4008,6 @@ class $$PreferencesTableTableTableManager extends RootTableManager<
|
|||||||
Value<int> id = const Value.absent(),
|
Value<int> id = const Value.absent(),
|
||||||
Value<SourceQualities> audioQuality = const Value.absent(),
|
Value<SourceQualities> audioQuality = const Value.absent(),
|
||||||
Value<bool> albumColorSync = const Value.absent(),
|
Value<bool> albumColorSync = const Value.absent(),
|
||||||
Value<bool> amoledDarkTheme = const Value.absent(),
|
|
||||||
Value<bool> checkUpdate = const Value.absent(),
|
Value<bool> checkUpdate = const Value.absent(),
|
||||||
Value<bool> normalizeAudio = const Value.absent(),
|
Value<bool> normalizeAudio = const Value.absent(),
|
||||||
Value<bool> showSystemTrayIcon = const Value.absent(),
|
Value<bool> showSystemTrayIcon = const Value.absent(),
|
||||||
@ -4068,19 +4022,18 @@ class $$PreferencesTableTableTableManager extends RootTableManager<
|
|||||||
Value<String> downloadLocation = const Value.absent(),
|
Value<String> downloadLocation = const Value.absent(),
|
||||||
Value<List<String>> localLibraryLocation = const Value.absent(),
|
Value<List<String>> localLibraryLocation = const Value.absent(),
|
||||||
Value<String> pipedInstance = const Value.absent(),
|
Value<String> pipedInstance = const Value.absent(),
|
||||||
|
Value<String> neteaseApiInstance = const Value.absent(),
|
||||||
Value<ThemeMode> themeMode = const Value.absent(),
|
Value<ThemeMode> themeMode = const Value.absent(),
|
||||||
Value<AudioSource> audioSource = const Value.absent(),
|
Value<AudioSource> audioSource = const Value.absent(),
|
||||||
Value<SourceCodecs> streamMusicCodec = const Value.absent(),
|
Value<SourceCodecs> streamMusicCodec = const Value.absent(),
|
||||||
Value<SourceCodecs> downloadMusicCodec = const Value.absent(),
|
Value<SourceCodecs> downloadMusicCodec = const Value.absent(),
|
||||||
Value<bool> discordPresence = const Value.absent(),
|
|
||||||
Value<bool> endlessPlayback = const Value.absent(),
|
Value<bool> endlessPlayback = const Value.absent(),
|
||||||
Value<bool> enableConnect = const Value.absent(),
|
Value<bool> playerWakelock = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
PreferencesTableCompanion(
|
PreferencesTableCompanion(
|
||||||
id: id,
|
id: id,
|
||||||
audioQuality: audioQuality,
|
audioQuality: audioQuality,
|
||||||
albumColorSync: albumColorSync,
|
albumColorSync: albumColorSync,
|
||||||
amoledDarkTheme: amoledDarkTheme,
|
|
||||||
checkUpdate: checkUpdate,
|
checkUpdate: checkUpdate,
|
||||||
normalizeAudio: normalizeAudio,
|
normalizeAudio: normalizeAudio,
|
||||||
showSystemTrayIcon: showSystemTrayIcon,
|
showSystemTrayIcon: showSystemTrayIcon,
|
||||||
@ -4095,19 +4048,18 @@ class $$PreferencesTableTableTableManager extends RootTableManager<
|
|||||||
downloadLocation: downloadLocation,
|
downloadLocation: downloadLocation,
|
||||||
localLibraryLocation: localLibraryLocation,
|
localLibraryLocation: localLibraryLocation,
|
||||||
pipedInstance: pipedInstance,
|
pipedInstance: pipedInstance,
|
||||||
|
neteaseApiInstance: neteaseApiInstance,
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
audioSource: audioSource,
|
audioSource: audioSource,
|
||||||
streamMusicCodec: streamMusicCodec,
|
streamMusicCodec: streamMusicCodec,
|
||||||
downloadMusicCodec: downloadMusicCodec,
|
downloadMusicCodec: downloadMusicCodec,
|
||||||
discordPresence: discordPresence,
|
|
||||||
endlessPlayback: endlessPlayback,
|
endlessPlayback: endlessPlayback,
|
||||||
enableConnect: enableConnect,
|
playerWakelock: playerWakelock,
|
||||||
),
|
),
|
||||||
createCompanionCallback: ({
|
createCompanionCallback: ({
|
||||||
Value<int> id = const Value.absent(),
|
Value<int> id = const Value.absent(),
|
||||||
Value<SourceQualities> audioQuality = const Value.absent(),
|
Value<SourceQualities> audioQuality = const Value.absent(),
|
||||||
Value<bool> albumColorSync = const Value.absent(),
|
Value<bool> albumColorSync = const Value.absent(),
|
||||||
Value<bool> amoledDarkTheme = const Value.absent(),
|
|
||||||
Value<bool> checkUpdate = const Value.absent(),
|
Value<bool> checkUpdate = const Value.absent(),
|
||||||
Value<bool> normalizeAudio = const Value.absent(),
|
Value<bool> normalizeAudio = const Value.absent(),
|
||||||
Value<bool> showSystemTrayIcon = const Value.absent(),
|
Value<bool> showSystemTrayIcon = const Value.absent(),
|
||||||
@ -4122,19 +4074,18 @@ class $$PreferencesTableTableTableManager extends RootTableManager<
|
|||||||
Value<String> downloadLocation = const Value.absent(),
|
Value<String> downloadLocation = const Value.absent(),
|
||||||
Value<List<String>> localLibraryLocation = const Value.absent(),
|
Value<List<String>> localLibraryLocation = const Value.absent(),
|
||||||
Value<String> pipedInstance = const Value.absent(),
|
Value<String> pipedInstance = const Value.absent(),
|
||||||
|
Value<String> neteaseApiInstance = const Value.absent(),
|
||||||
Value<ThemeMode> themeMode = const Value.absent(),
|
Value<ThemeMode> themeMode = const Value.absent(),
|
||||||
Value<AudioSource> audioSource = const Value.absent(),
|
Value<AudioSource> audioSource = const Value.absent(),
|
||||||
Value<SourceCodecs> streamMusicCodec = const Value.absent(),
|
Value<SourceCodecs> streamMusicCodec = const Value.absent(),
|
||||||
Value<SourceCodecs> downloadMusicCodec = const Value.absent(),
|
Value<SourceCodecs> downloadMusicCodec = const Value.absent(),
|
||||||
Value<bool> discordPresence = const Value.absent(),
|
|
||||||
Value<bool> endlessPlayback = const Value.absent(),
|
Value<bool> endlessPlayback = const Value.absent(),
|
||||||
Value<bool> enableConnect = const Value.absent(),
|
Value<bool> playerWakelock = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
PreferencesTableCompanion.insert(
|
PreferencesTableCompanion.insert(
|
||||||
id: id,
|
id: id,
|
||||||
audioQuality: audioQuality,
|
audioQuality: audioQuality,
|
||||||
albumColorSync: albumColorSync,
|
albumColorSync: albumColorSync,
|
||||||
amoledDarkTheme: amoledDarkTheme,
|
|
||||||
checkUpdate: checkUpdate,
|
checkUpdate: checkUpdate,
|
||||||
normalizeAudio: normalizeAudio,
|
normalizeAudio: normalizeAudio,
|
||||||
showSystemTrayIcon: showSystemTrayIcon,
|
showSystemTrayIcon: showSystemTrayIcon,
|
||||||
@ -4149,13 +4100,13 @@ class $$PreferencesTableTableTableManager extends RootTableManager<
|
|||||||
downloadLocation: downloadLocation,
|
downloadLocation: downloadLocation,
|
||||||
localLibraryLocation: localLibraryLocation,
|
localLibraryLocation: localLibraryLocation,
|
||||||
pipedInstance: pipedInstance,
|
pipedInstance: pipedInstance,
|
||||||
|
neteaseApiInstance: neteaseApiInstance,
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
audioSource: audioSource,
|
audioSource: audioSource,
|
||||||
streamMusicCodec: streamMusicCodec,
|
streamMusicCodec: streamMusicCodec,
|
||||||
downloadMusicCodec: downloadMusicCodec,
|
downloadMusicCodec: downloadMusicCodec,
|
||||||
discordPresence: discordPresence,
|
|
||||||
endlessPlayback: endlessPlayback,
|
endlessPlayback: endlessPlayback,
|
||||||
enableConnect: enableConnect,
|
playerWakelock: playerWakelock,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -4180,11 +4131,6 @@ class $$PreferencesTableTableFilterComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnFilters<bool> get amoledDarkTheme => $state.composableBuilder(
|
|
||||||
column: $state.table.amoledDarkTheme,
|
|
||||||
builder: (column, joinBuilders) =>
|
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
|
||||||
|
|
||||||
ColumnFilters<bool> get checkUpdate => $state.composableBuilder(
|
ColumnFilters<bool> get checkUpdate => $state.composableBuilder(
|
||||||
column: $state.table.checkUpdate,
|
column: $state.table.checkUpdate,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
@ -4269,6 +4215,11 @@ class $$PreferencesTableTableFilterComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
|
ColumnFilters<String> get neteaseApiInstance => $state.composableBuilder(
|
||||||
|
column: $state.table.neteaseApiInstance,
|
||||||
|
builder: (column, joinBuilders) =>
|
||||||
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnWithTypeConverterFilters<ThemeMode, ThemeMode, String> get themeMode =>
|
ColumnWithTypeConverterFilters<ThemeMode, ThemeMode, String> get themeMode =>
|
||||||
$state.composableBuilder(
|
$state.composableBuilder(
|
||||||
column: $state.table.themeMode,
|
column: $state.table.themeMode,
|
||||||
@ -4297,18 +4248,13 @@ class $$PreferencesTableTableFilterComposer
|
|||||||
column,
|
column,
|
||||||
joinBuilders: joinBuilders));
|
joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnFilters<bool> get discordPresence => $state.composableBuilder(
|
|
||||||
column: $state.table.discordPresence,
|
|
||||||
builder: (column, joinBuilders) =>
|
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
|
||||||
|
|
||||||
ColumnFilters<bool> get endlessPlayback => $state.composableBuilder(
|
ColumnFilters<bool> get endlessPlayback => $state.composableBuilder(
|
||||||
column: $state.table.endlessPlayback,
|
column: $state.table.endlessPlayback,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnFilters<bool> get enableConnect => $state.composableBuilder(
|
ColumnFilters<bool> get playerWakelock => $state.composableBuilder(
|
||||||
column: $state.table.enableConnect,
|
column: $state.table.playerWakelock,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnFilters(column, joinBuilders: joinBuilders));
|
ColumnFilters(column, joinBuilders: joinBuilders));
|
||||||
}
|
}
|
||||||
@ -4331,11 +4277,6 @@ class $$PreferencesTableTableOrderingComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnOrderings<bool> get amoledDarkTheme => $state.composableBuilder(
|
|
||||||
column: $state.table.amoledDarkTheme,
|
|
||||||
builder: (column, joinBuilders) =>
|
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
|
||||||
|
|
||||||
ColumnOrderings<bool> get checkUpdate => $state.composableBuilder(
|
ColumnOrderings<bool> get checkUpdate => $state.composableBuilder(
|
||||||
column: $state.table.checkUpdate,
|
column: $state.table.checkUpdate,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
@ -4406,6 +4347,11 @@ class $$PreferencesTableTableOrderingComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
|
ColumnOrderings<String> get neteaseApiInstance => $state.composableBuilder(
|
||||||
|
column: $state.table.neteaseApiInstance,
|
||||||
|
builder: (column, joinBuilders) =>
|
||||||
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnOrderings<String> get themeMode => $state.composableBuilder(
|
ColumnOrderings<String> get themeMode => $state.composableBuilder(
|
||||||
column: $state.table.themeMode,
|
column: $state.table.themeMode,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
@ -4426,18 +4372,13 @@ class $$PreferencesTableTableOrderingComposer
|
|||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnOrderings<bool> get discordPresence => $state.composableBuilder(
|
|
||||||
column: $state.table.discordPresence,
|
|
||||||
builder: (column, joinBuilders) =>
|
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
|
||||||
|
|
||||||
ColumnOrderings<bool> get endlessPlayback => $state.composableBuilder(
|
ColumnOrderings<bool> get endlessPlayback => $state.composableBuilder(
|
||||||
column: $state.table.endlessPlayback,
|
column: $state.table.endlessPlayback,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
|
|
||||||
ColumnOrderings<bool> get enableConnect => $state.composableBuilder(
|
ColumnOrderings<bool> get playerWakelock => $state.composableBuilder(
|
||||||
column: $state.table.enableConnect,
|
column: $state.table.playerWakelock,
|
||||||
builder: (column, joinBuilders) =>
|
builder: (column, joinBuilders) =>
|
||||||
ColumnOrderings(column, joinBuilders: joinBuilders));
|
ColumnOrderings(column, joinBuilders: joinBuilders));
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ enum CloseBehavior {
|
|||||||
|
|
||||||
enum AudioSource {
|
enum AudioSource {
|
||||||
youtube,
|
youtube,
|
||||||
piped;
|
piped,
|
||||||
|
netease;
|
||||||
|
|
||||||
String get label => name[0].toUpperCase() + name.substring(1);
|
String get label => name[0].toUpperCase() + name.substring(1);
|
||||||
}
|
}
|
||||||
@ -45,8 +46,6 @@ class PreferencesTable extends Table {
|
|||||||
.withDefault(Constant(SourceQualities.high.name))();
|
.withDefault(Constant(SourceQualities.high.name))();
|
||||||
BoolColumn get albumColorSync =>
|
BoolColumn get albumColorSync =>
|
||||||
boolean().withDefault(const Constant(true))();
|
boolean().withDefault(const Constant(true))();
|
||||||
BoolColumn get amoledDarkTheme =>
|
|
||||||
boolean().withDefault(const Constant(false))();
|
|
||||||
BoolColumn get checkUpdate => boolean().withDefault(const Constant(true))();
|
BoolColumn get checkUpdate => boolean().withDefault(const Constant(true))();
|
||||||
BoolColumn get normalizeAudio =>
|
BoolColumn get normalizeAudio =>
|
||||||
boolean().withDefault(const Constant(false))();
|
boolean().withDefault(const Constant(false))();
|
||||||
@ -76,6 +75,8 @@ class PreferencesTable extends Table {
|
|||||||
text().withDefault(const Constant('')).map(const StringListConverter())();
|
text().withDefault(const Constant('')).map(const StringListConverter())();
|
||||||
TextColumn get pipedInstance =>
|
TextColumn get pipedInstance =>
|
||||||
text().withDefault(const Constant('https://pipedapi.kavin.rocks'))();
|
text().withDefault(const Constant('https://pipedapi.kavin.rocks'))();
|
||||||
|
TextColumn get neteaseApiInstance => text().withDefault(
|
||||||
|
const Constant('https://rhythmbox-netease-music-api.vercel.app'))();
|
||||||
TextColumn get themeMode =>
|
TextColumn get themeMode =>
|
||||||
textEnum<ThemeMode>().withDefault(Constant(ThemeMode.system.name))();
|
textEnum<ThemeMode>().withDefault(Constant(ThemeMode.system.name))();
|
||||||
TextColumn get audioSource =>
|
TextColumn get audioSource =>
|
||||||
@ -84,12 +85,10 @@ class PreferencesTable extends Table {
|
|||||||
textEnum<SourceCodecs>().withDefault(Constant(SourceCodecs.weba.name))();
|
textEnum<SourceCodecs>().withDefault(Constant(SourceCodecs.weba.name))();
|
||||||
TextColumn get downloadMusicCodec =>
|
TextColumn get downloadMusicCodec =>
|
||||||
textEnum<SourceCodecs>().withDefault(Constant(SourceCodecs.m4a.name))();
|
textEnum<SourceCodecs>().withDefault(Constant(SourceCodecs.m4a.name))();
|
||||||
BoolColumn get discordPresence =>
|
|
||||||
boolean().withDefault(const Constant(true))();
|
|
||||||
BoolColumn get endlessPlayback =>
|
BoolColumn get endlessPlayback =>
|
||||||
boolean().withDefault(const Constant(true))();
|
boolean().withDefault(const Constant(true))();
|
||||||
BoolColumn get enableConnect =>
|
BoolColumn get playerWakelock =>
|
||||||
boolean().withDefault(const Constant(false))();
|
boolean().withDefault(const Constant(true))();
|
||||||
|
|
||||||
// Default values as PreferencesTableData
|
// Default values as PreferencesTableData
|
||||||
static PreferencesTableData defaults() {
|
static PreferencesTableData defaults() {
|
||||||
@ -97,7 +96,6 @@ class PreferencesTable extends Table {
|
|||||||
id: 0,
|
id: 0,
|
||||||
audioQuality: SourceQualities.high,
|
audioQuality: SourceQualities.high,
|
||||||
albumColorSync: true,
|
albumColorSync: true,
|
||||||
amoledDarkTheme: false,
|
|
||||||
checkUpdate: true,
|
checkUpdate: true,
|
||||||
normalizeAudio: false,
|
normalizeAudio: false,
|
||||||
showSystemTrayIcon: false,
|
showSystemTrayIcon: false,
|
||||||
@ -111,14 +109,14 @@ class PreferencesTable extends Table {
|
|||||||
searchMode: SearchMode.youtube,
|
searchMode: SearchMode.youtube,
|
||||||
downloadLocation: '',
|
downloadLocation: '',
|
||||||
localLibraryLocation: [],
|
localLibraryLocation: [],
|
||||||
|
neteaseApiInstance: 'https://rhythmbox-netease-music-api.vercel.app',
|
||||||
pipedInstance: 'https://pipedapi.kavin.rocks',
|
pipedInstance: 'https://pipedapi.kavin.rocks',
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.system,
|
||||||
audioSource: AudioSource.youtube,
|
audioSource: AudioSource.youtube,
|
||||||
streamMusicCodec: SourceCodecs.weba,
|
streamMusicCodec: SourceCodecs.weba,
|
||||||
downloadMusicCodec: SourceCodecs.m4a,
|
downloadMusicCodec: SourceCodecs.m4a,
|
||||||
discordPresence: true,
|
|
||||||
endlessPlayback: true,
|
endlessPlayback: true,
|
||||||
enableConnect: false,
|
playerWakelock: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ part of '../database.dart';
|
|||||||
|
|
||||||
enum SourceType {
|
enum SourceType {
|
||||||
youtube._('YouTube'),
|
youtube._('YouTube'),
|
||||||
youtubeMusic._('YouTube Music');
|
youtubeMusic._('YouTube Music'),
|
||||||
|
netease._('Netease Music');
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:lrc/lrc.dart';
|
import 'package:lrc/lrc.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/lyrics/model.dart';
|
import 'package:rhythm_box/services/lyrics/model.dart';
|
||||||
@ -164,8 +163,8 @@ class SyncedLyricsProvider extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return lyrics;
|
return lyrics;
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stack) {
|
||||||
log('[Lyrics] Error: $e; Trace:\n$stackTrace');
|
Get.find<ErrorNotifier>().logError('[Lyrics] Error: $e', trace: stack);
|
||||||
return SubtitleSimple(
|
return SubtitleSimple(
|
||||||
uri: Uri.parse('https://example.com/not-found'),
|
uri: Uri.parse('https://example.com/not-found'),
|
||||||
name: 'Lyrics Not Found',
|
name: 'Lyrics Not Found',
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/custom_player.dart';
|
import 'package:rhythm_box/services/audio_player/custom_player.dart';
|
||||||
import 'package:rhythm_box/services/local_track.dart';
|
import 'package:rhythm_box/services/local_track.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
@ -85,7 +86,7 @@ abstract class AudioPlayerInterface {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_mkPlayer.stream.error.listen((event) {
|
_mkPlayer.stream.error.listen((event) {
|
||||||
log('[Playback] Error: $event');
|
Get.find<ErrorNotifier>().logError('[Playback][Media] Error: $event');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
@ -41,7 +40,9 @@ class ActiveSourcedTrackProvider extends GetxController {
|
|||||||
await audioPlayer.removeTrack(oldActiveIndex);
|
await audioPlayer.removeTrack(oldActiveIndex);
|
||||||
await playback.jumpToTrack(newTrack);
|
await playback.jumpToTrack(newTrack);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[Playback] Failed to swap with siblings. Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>().logError(
|
||||||
|
'[Playback] Failed to swap with siblings. Error: $e',
|
||||||
|
trace: stack);
|
||||||
} finally {
|
} finally {
|
||||||
query.isQueryingTrackInfo.value = false;
|
query.isQueryingTrackInfo.value = false;
|
||||||
await audioPlayer.resume();
|
await audioPlayer.resume();
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart' hide Response;
|
import 'package:dio/dio.dart' hide Response;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:get/get.dart' hide Response;
|
import 'package:get/get.dart' hide Response;
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
import 'package:rhythm_box/services/server/sourced_track.dart';
|
import 'package:rhythm_box/services/server/sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sources/netease.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
|
|
||||||
class ServerPlaybackRoutesProvider {
|
class ServerPlaybackRoutesProvider {
|
||||||
@ -25,14 +25,25 @@ class ServerPlaybackRoutesProvider {
|
|||||||
|
|
||||||
activeSourcedTrack.updateTrack(sourcedTrack);
|
activeSourcedTrack.updateTrack(sourcedTrack);
|
||||||
|
|
||||||
|
var url = sourcedTrack!.url;
|
||||||
|
|
||||||
|
if (sourcedTrack is NeteaseSourcedTrack) {
|
||||||
|
// Special processing for netease to get real assets url
|
||||||
|
final resp = await GetConnect(timeout: const Duration(seconds: 30)).get(
|
||||||
|
'${sourcedTrack.url}&realIP=${await NeteaseSourcedTrack.lookupRealIp()}',
|
||||||
|
);
|
||||||
|
final realUrl = resp.body['data'][0]['url'];
|
||||||
|
url = realUrl;
|
||||||
|
}
|
||||||
|
|
||||||
final res = await Dio().get(
|
final res = await Dio().get(
|
||||||
sourcedTrack!.url,
|
url,
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
...request.headers,
|
...request.headers,
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
|
||||||
'host': Uri.parse(sourcedTrack.url).host,
|
'host': Uri.parse(url).host,
|
||||||
'Cache-Control': 'max-age=0',
|
'Cache-Control': 'max-age=0',
|
||||||
'Connection': 'keep-alive',
|
'Connection': 'keep-alive',
|
||||||
},
|
},
|
||||||
@ -57,8 +68,9 @@ class ServerPlaybackRoutesProvider {
|
|||||||
},
|
},
|
||||||
headers: res.headers.map,
|
headers: res.headers.map,
|
||||||
);
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stack) {
|
||||||
log('[PlaybackSever] Error: $e; Trace:\n $stackTrace');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[PlaybackSever] Error: $e', trace: stack);
|
||||||
return Response.internalServerError();
|
return Response.internalServerError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/user_preferences.dart';
|
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sources/netease.dart';
|
||||||
import 'package:rhythm_box/services/utils.dart';
|
import 'package:rhythm_box/services/utils.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
@ -55,6 +56,12 @@ abstract class SourcedTrack extends Track {
|
|||||||
.cast<SourceInfo>();
|
.cast<SourceInfo>();
|
||||||
|
|
||||||
return switch (audioSource) {
|
return switch (audioSource) {
|
||||||
|
AudioSource.netease => NeteaseSourcedTrack(
|
||||||
|
source: source,
|
||||||
|
siblings: siblings,
|
||||||
|
sourceInfo: sourceInfo,
|
||||||
|
track: track,
|
||||||
|
),
|
||||||
AudioSource.piped => PipedSourcedTrack(
|
AudioSource.piped => PipedSourcedTrack(
|
||||||
source: source,
|
source: source,
|
||||||
siblings: siblings,
|
siblings: siblings,
|
||||||
@ -94,14 +101,20 @@ abstract class SourcedTrack extends Track {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return switch (audioSource) {
|
return switch (audioSource) {
|
||||||
|
AudioSource.netease =>
|
||||||
|
await NeteaseSourcedTrack.fetchFromTrack(track: track),
|
||||||
AudioSource.piped =>
|
AudioSource.piped =>
|
||||||
await PipedSourcedTrack.fetchFromTrack(track: track),
|
await PipedSourcedTrack.fetchFromTrack(track: track),
|
||||||
_ => await YoutubeSourcedTrack.fetchFromTrack(track: track),
|
_ => await YoutubeSourcedTrack.fetchFromTrack(track: track),
|
||||||
};
|
};
|
||||||
} on TrackNotFoundError catch (_) {
|
} on TrackNotFoundError catch (_) {
|
||||||
// TODO Try to look it up in other source
|
return switch (preferences.audioSource) {
|
||||||
// But the youtube and piped.video are the same, and there is no extra sources, so i ignored this for temporary
|
AudioSource.piped ||
|
||||||
rethrow;
|
AudioSource.youtube =>
|
||||||
|
await NeteaseSourcedTrack.fetchFromTrack(track: track),
|
||||||
|
AudioSource.netease =>
|
||||||
|
await YoutubeSourcedTrack.fetchFromTrack(track: track),
|
||||||
|
};
|
||||||
} on HttpClientClosedException catch (_) {
|
} on HttpClientClosedException catch (_) {
|
||||||
return await PipedSourcedTrack.fetchFromTrack(track: track);
|
return await PipedSourcedTrack.fetchFromTrack(track: track);
|
||||||
} on VideoUnplayableException catch (_) {
|
} on VideoUnplayableException catch (_) {
|
||||||
|
235
lib/services/sourced_track/sources/netease.dart
Executable file
235
lib/services/sourced_track/sources/netease.dart
Executable file
@ -0,0 +1,235 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:get/get.dart' hide Value;
|
||||||
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||||
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/enums.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/exceptions.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/models/source_map.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
|
||||||
|
class NeteaseSourceInfo extends SourceInfo {
|
||||||
|
NeteaseSourceInfo({
|
||||||
|
required super.id,
|
||||||
|
required super.title,
|
||||||
|
required super.artist,
|
||||||
|
required super.thumbnail,
|
||||||
|
required super.pageUrl,
|
||||||
|
required super.duration,
|
||||||
|
required super.artistUrl,
|
||||||
|
required super.album,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class NeteaseSourcedTrack extends SourcedTrack {
|
||||||
|
NeteaseSourcedTrack({
|
||||||
|
required super.source,
|
||||||
|
required super.siblings,
|
||||||
|
required super.sourceInfo,
|
||||||
|
required super.track,
|
||||||
|
});
|
||||||
|
|
||||||
|
static String getBaseUrl() {
|
||||||
|
final preferences = Get.find<UserPreferencesProvider>().state.value;
|
||||||
|
return preferences.neteaseApiInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GetConnect getClient() {
|
||||||
|
final client = GetConnect();
|
||||||
|
client.baseUrl = getBaseUrl();
|
||||||
|
client.timeout = const Duration(seconds: 30);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String? _lookedUpRealIp;
|
||||||
|
|
||||||
|
static Future<String> lookupRealIp() async {
|
||||||
|
if (_lookedUpRealIp != null) return _lookedUpRealIp!;
|
||||||
|
const ipCheckUrl = 'https://api.ipify.org';
|
||||||
|
final client = GetConnect(timeout: const Duration(seconds: 30));
|
||||||
|
final resp = await client.get(ipCheckUrl);
|
||||||
|
_lookedUpRealIp = resp.body;
|
||||||
|
return _lookedUpRealIp!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<NeteaseSourcedTrack> fetchFromTrack({
|
||||||
|
required Track track,
|
||||||
|
}) async {
|
||||||
|
final DatabaseProvider db = Get.find();
|
||||||
|
final cachedSource = await (db.database.select(db.database.sourceMatchTable)
|
||||||
|
..where((s) => s.trackId.equals(track.id!))
|
||||||
|
..limit(1)
|
||||||
|
..orderBy([
|
||||||
|
(s) =>
|
||||||
|
OrderingTerm(expression: s.createdAt, mode: OrderingMode.desc),
|
||||||
|
]))
|
||||||
|
.get()
|
||||||
|
.then((s) => s.firstOrNull);
|
||||||
|
|
||||||
|
if (cachedSource == null) {
|
||||||
|
final siblings = await fetchSiblings(track: track);
|
||||||
|
if (siblings.isEmpty) {
|
||||||
|
throw TrackNotFoundError(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.database.into(db.database.sourceMatchTable).insert(
|
||||||
|
SourceMatchTableCompanion.insert(
|
||||||
|
trackId: track.id!,
|
||||||
|
sourceId: siblings.first.info.id,
|
||||||
|
sourceType: const Value(SourceType.netease),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return NeteaseSourcedTrack(
|
||||||
|
siblings: siblings.map((s) => s.info).skip(1).toList(),
|
||||||
|
source: siblings.first.source as SourceMap,
|
||||||
|
sourceInfo: siblings.first.info,
|
||||||
|
track: track,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final client = getClient();
|
||||||
|
final resp = await client.get('/song/detail?ids=${cachedSource.sourceId}');
|
||||||
|
final item = resp.body['songs'][0];
|
||||||
|
|
||||||
|
return NeteaseSourcedTrack(
|
||||||
|
siblings: [],
|
||||||
|
source: toSourceMap(item),
|
||||||
|
sourceInfo: NeteaseSourceInfo(
|
||||||
|
id: item['id'].toString(),
|
||||||
|
artist: item['ar'].map((x) => x['name']).join(','),
|
||||||
|
artistUrl: 'https://music.163.com/#/artist?id=${item['ar'][0]['id']}',
|
||||||
|
pageUrl: 'https://music.163.com/#/song?id=${item['id']}',
|
||||||
|
thumbnail: item['al']['picUrl'],
|
||||||
|
title: item['name'],
|
||||||
|
duration: Duration(milliseconds: item['dt']),
|
||||||
|
album: item['al']['name'],
|
||||||
|
),
|
||||||
|
track: track,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SourceMap toSourceMap(dynamic manifest) {
|
||||||
|
final baseUrl = getBaseUrl();
|
||||||
|
|
||||||
|
// Due to netease may provide m4a, mp3 and others, we cannot decide this so mock this data.
|
||||||
|
return SourceMap(
|
||||||
|
m4a: SourceQualityMap(
|
||||||
|
high: '$baseUrl/song/url?id=${manifest['id']}',
|
||||||
|
medium: '$baseUrl/song/url?id=${manifest['id']}&br=192000',
|
||||||
|
low: '$baseUrl/song/url?id=${manifest['id']}&br=128000',
|
||||||
|
),
|
||||||
|
weba: SourceQualityMap(
|
||||||
|
high: '$baseUrl/song/url?id=${manifest['id']}',
|
||||||
|
medium: '$baseUrl/song/url?id=${manifest['id']}&br=192000',
|
||||||
|
low: '$baseUrl/song/url?id=${manifest['id']}&br=128000',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<SiblingType>> fetchSiblings({
|
||||||
|
required Track track,
|
||||||
|
}) async {
|
||||||
|
final query = SourcedTrack.getSearchTerm(track);
|
||||||
|
|
||||||
|
final client = getClient();
|
||||||
|
final resp =
|
||||||
|
await client.get('/search?keywords=${Uri.encodeComponent(query)}');
|
||||||
|
final results = resp.body['result']['songs'];
|
||||||
|
|
||||||
|
// We can just trust netease music for now
|
||||||
|
// If we need to check is the result correct, refer to this code
|
||||||
|
// https://github.com/KRTirtho/spotube/blob/9b024120601c0d381edeab4460cb22f87149d0f8/lib/services/sourced_track/sources/jiosaavn.dart#L129
|
||||||
|
final matchedResults = results.map(toSiblingType).toList();
|
||||||
|
|
||||||
|
return matchedResults.cast<SiblingType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NeteaseSourcedTrack> copyWithSibling() async {
|
||||||
|
if (siblings.isNotEmpty) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
final fetchedSiblings = await fetchSiblings(track: this);
|
||||||
|
|
||||||
|
return NeteaseSourcedTrack(
|
||||||
|
siblings: fetchedSiblings
|
||||||
|
.where((s) => s.info.id != sourceInfo.id)
|
||||||
|
.map((s) => s.info)
|
||||||
|
.toList(),
|
||||||
|
source: source,
|
||||||
|
sourceInfo: sourceInfo,
|
||||||
|
track: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NeteaseSourcedTrack?> swapWithSibling(SourceInfo sibling) async {
|
||||||
|
if (sibling.id == sourceInfo.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a sibling source that was fetched from the search results
|
||||||
|
final isStepSibling = siblings.none((s) => s.id == sibling.id);
|
||||||
|
|
||||||
|
final newSourceInfo = isStepSibling
|
||||||
|
? sibling
|
||||||
|
: siblings.firstWhere((s) => s.id == sibling.id);
|
||||||
|
final newSiblings = siblings.where((s) => s.id != sibling.id).toList()
|
||||||
|
..insert(0, sourceInfo);
|
||||||
|
|
||||||
|
final client = getClient();
|
||||||
|
final resp = await client.get('/song/detail?ids=${newSourceInfo.id}');
|
||||||
|
final item = resp.body['songs'][0];
|
||||||
|
|
||||||
|
final (:info, :source) = toSiblingType(item);
|
||||||
|
|
||||||
|
final db = Get.find<DatabaseProvider>();
|
||||||
|
await db.database.into(db.database.sourceMatchTable).insert(
|
||||||
|
SourceMatchTableCompanion.insert(
|
||||||
|
trackId: id!,
|
||||||
|
sourceId: info.id,
|
||||||
|
sourceType: const Value(SourceType.netease),
|
||||||
|
// Because we're sorting by createdAt in the query
|
||||||
|
// we have to update it to indicate priority
|
||||||
|
createdAt: Value(DateTime.now()),
|
||||||
|
),
|
||||||
|
mode: InsertMode.replace,
|
||||||
|
);
|
||||||
|
|
||||||
|
return NeteaseSourcedTrack(
|
||||||
|
siblings: newSiblings,
|
||||||
|
source: source!,
|
||||||
|
sourceInfo: info,
|
||||||
|
track: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SiblingType toSiblingType(dynamic item) {
|
||||||
|
final firstArtist = item['ar'] != null ? item['ar'][0] : item['artists'][0];
|
||||||
|
|
||||||
|
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.zero,
|
||||||
|
album: item['al']?['name'],
|
||||||
|
),
|
||||||
|
source: toSourceMap(item),
|
||||||
|
);
|
||||||
|
|
||||||
|
return sibling;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:get/get.dart' hide Value;
|
import 'package:get/get.dart' hide Value;
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/utils.dart';
|
import 'package:rhythm_box/services/utils.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
@ -86,7 +85,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
cachedSource.sourceId,
|
cachedSource.sourceId,
|
||||||
)
|
)
|
||||||
.timeout(
|
.timeout(
|
||||||
const Duration(seconds: 5),
|
const Duration(seconds: 30),
|
||||||
onTimeout: () => throw ClientException('Timeout'),
|
onTimeout: () => throw ClientException('Timeout'),
|
||||||
);
|
);
|
||||||
return YoutubeSourcedTrack(
|
return YoutubeSourcedTrack(
|
||||||
@ -141,7 +140,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
final manifest =
|
final manifest =
|
||||||
await youtubeClient.videos.streamsClient.getManifest(item.id).timeout(
|
await youtubeClient.videos.streamsClient.getManifest(item.id).timeout(
|
||||||
const Duration(seconds: 5),
|
const Duration(seconds: 30),
|
||||||
onTimeout: () => throw ClientException('Timeout'),
|
onTimeout: () => throw ClientException('Timeout'),
|
||||||
);
|
);
|
||||||
sourceMap = toSourceMap(manifest);
|
sourceMap = toSourceMap(manifest);
|
||||||
@ -242,7 +241,8 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
];
|
];
|
||||||
} on VideoUnplayableException catch (e) {
|
} on VideoUnplayableException catch (e) {
|
||||||
// Ignore this error and continue with the search
|
// Ignore this error and continue with the search
|
||||||
log('[Source][YoutubeMusic] Unable to search data: $e');
|
Get.find<ErrorNotifier>().logError(
|
||||||
|
'[Source][YoutubeMusic] Unable to play stream on youtube: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
|
|
||||||
final searchResults = await youtubeClient.search.search(
|
final searchResults = await youtubeClient.search.search(
|
||||||
query,
|
query,
|
||||||
filter: const SearchFilter('CAMSAhAB'),
|
filter: TypeFilters.video,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ServiceUtils.onlyContainsEnglish(query)) {
|
if (ServiceUtils.onlyContainsEnglish(query)) {
|
||||||
@ -285,7 +285,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
final manifest = await youtubeClient.videos.streamsClient
|
final manifest = await youtubeClient.videos.streamsClient
|
||||||
.getManifest(newSourceInfo.id)
|
.getManifest(newSourceInfo.id)
|
||||||
.timeout(
|
.timeout(
|
||||||
const Duration(seconds: 5),
|
const Duration(seconds: 30),
|
||||||
onTimeout: () => throw ClientException('Timeout'),
|
onTimeout: () => throw ClientException('Timeout'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -39,11 +39,12 @@ class WindowManagerTools with WidgetsBindingObserver {
|
|||||||
WidgetsBinding.instance.addObserver(instance);
|
WidgetsBinding.instance.addObserver(instance);
|
||||||
|
|
||||||
await windowManager.waitUntilReadyToShow(
|
await windowManager.waitUntilReadyToShow(
|
||||||
const WindowOptions(
|
WindowOptions(
|
||||||
title: 'RhythmBox',
|
title: 'RhythmBox',
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
minimumSize: Size(300, 700),
|
minimumSize: const Size(300, 700),
|
||||||
titleBarStyle: TitleBarStyle.hidden,
|
titleBarStyle:
|
||||||
|
PlatformInfo.isMacOS ? TitleBarStyle.hidden : TitleBarStyle.normal,
|
||||||
center: true,
|
center: true,
|
||||||
),
|
),
|
||||||
() async {
|
() async {
|
||||||
|
@ -1,12 +1,43 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
class SystemShell extends StatelessWidget {
|
class SystemShell extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const SystemShell({super.key, required this.child});
|
const SystemShell({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SystemShell> createState() => _SystemShellState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SystemShellState extends State<SystemShell> {
|
||||||
|
late final ErrorNotifier _errorNotifier = Get.find();
|
||||||
|
|
||||||
|
StreamSubscription? _subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_subscription = _errorNotifier.showing.listen((value) {
|
||||||
|
if (value == null) {
|
||||||
|
ScaffoldMessenger.of(context).clearMaterialBanners();
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showMaterialBanner(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subscription?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (PlatformInfo.isMacOS) {
|
if (PlatformInfo.isMacOS) {
|
||||||
@ -21,12 +52,12 @@ class SystemShell extends StatelessWidget {
|
|||||||
thickness: 0.3,
|
thickness: 0.3,
|
||||||
height: 0.3,
|
height: 0.3,
|
||||||
),
|
),
|
||||||
Expanded(child: child),
|
Expanded(child: widget.child),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
return widget.child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,25 +50,27 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.5);
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.5);
|
||||||
|
|
||||||
void _syncLyricsProgress() {
|
void _syncLyricsProgress() {
|
||||||
for (var idx = 0; idx < _lyric!.lyrics.length; idx++) {
|
if (_isLyricSynced) {
|
||||||
final lyricSlice = _lyric!.lyrics[idx];
|
for (var idx = 0; idx < _lyric!.lyrics.length; idx++) {
|
||||||
final lyricNextSlice =
|
final lyricSlice = _lyric!.lyrics[idx];
|
||||||
idx + 1 < _lyric!.lyrics.length ? _lyric!.lyrics[idx + 1] : null;
|
final lyricNextSlice =
|
||||||
final isActive = _playback.durationCurrent.value.inSeconds >=
|
idx + 1 < _lyric!.lyrics.length ? _lyric!.lyrics[idx + 1] : null;
|
||||||
lyricSlice.time.inSeconds &&
|
final isActive = _playback.durationCurrent.value.inSeconds >=
|
||||||
(lyricNextSlice == null ||
|
lyricSlice.time.inSeconds &&
|
||||||
lyricNextSlice.time.inSeconds >
|
(lyricNextSlice == null ||
|
||||||
_playback.durationCurrent.value.inSeconds);
|
lyricNextSlice.time.inSeconds >
|
||||||
if (isActive) {
|
_playback.durationCurrent.value.inSeconds);
|
||||||
_autoScrollController.scrollToIndex(
|
if (isActive) {
|
||||||
idx,
|
_autoScrollController.scrollToIndex(
|
||||||
preferPosition: AutoScrollPosition.middle,
|
idx,
|
||||||
);
|
preferPosition: AutoScrollPosition.middle,
|
||||||
return;
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lyric!.lyrics.isNotEmpty) {
|
if (_lyric!.lyrics.isNotEmpty || !_isLyricSynced) {
|
||||||
_autoScrollController.scrollToIndex(
|
_autoScrollController.scrollToIndex(
|
||||||
0,
|
0,
|
||||||
preferPosition: AutoScrollPosition.begin,
|
preferPosition: AutoScrollPosition.begin,
|
||||||
@ -120,6 +122,18 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (_lyric != null && _lyric!.lyrics.isNotEmpty && !_isLyricSynced)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Text(
|
||||||
|
'Lyrics isn\'t synced',
|
||||||
|
textAlign: MediaQuery.of(context).size.width >= 720
|
||||||
|
? TextAlign.center
|
||||||
|
: TextAlign.left,
|
||||||
|
).paddingSymmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (_lyric != null && _lyric!.lyrics.isNotEmpty)
|
if (_lyric != null && _lyric!.lyrics.isNotEmpty)
|
||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemCount: _lyric!.lyrics.length,
|
itemCount: _lyric!.lyrics.length,
|
||||||
@ -132,7 +146,8 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
lyricSlice.time.inSeconds &&
|
lyricSlice.time.inSeconds &&
|
||||||
(lyricNextSlice == null ||
|
(lyricNextSlice == null ||
|
||||||
lyricNextSlice.time.inSeconds >
|
lyricNextSlice.time.inSeconds >
|
||||||
_playback.durationCurrent.value.inSeconds);
|
_playback.durationCurrent.value.inSeconds) &&
|
||||||
|
_isLyricSynced;
|
||||||
|
|
||||||
if (_playback.durationCurrent.value.inSeconds ==
|
if (_playback.durationCurrent.value.inSeconds ==
|
||||||
lyricSlice.time.inSeconds &&
|
lyricSlice.time.inSeconds &&
|
||||||
@ -142,6 +157,7 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
preferPosition: AutoScrollPosition.middle,
|
preferPosition: AutoScrollPosition.middle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AutoScrollTag(
|
return AutoScrollTag(
|
||||||
key: ValueKey(idx),
|
key: ValueKey(idx),
|
||||||
index: idx,
|
index: idx,
|
||||||
@ -215,6 +231,7 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Lyrics Not Found',
|
'Lyrics Not Found',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
const Text(
|
const Text(
|
||||||
|
@ -46,6 +46,8 @@ class _BottomPlayerState extends State<BottomPlayer>
|
|||||||
late final AudioPlayerProvider _playback = Get.find();
|
late final AudioPlayerProvider _playback = Get.find();
|
||||||
late final QueryingTrackInfoProvider _query = Get.find();
|
late final QueryingTrackInfoProvider _query = Get.find();
|
||||||
|
|
||||||
|
bool get _isFetchingActiveTrack => _query.isQueryingTrackInfo.value;
|
||||||
|
|
||||||
String? get _albumArt =>
|
String? get _albumArt =>
|
||||||
(_playback.state.value.activeTrack?.album?.images).asUrlString(
|
(_playback.state.value.activeTrack?.album?.images).asUrlString(
|
||||||
index:
|
index:
|
||||||
@ -102,19 +104,18 @@ class _BottomPlayerState extends State<BottomPlayer>
|
|||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (_playback.durationCurrent.value != Duration.zero)
|
TweenAnimationBuilder<double>(
|
||||||
TweenAnimationBuilder<double>(
|
tween: Tween(
|
||||||
tween: Tween(
|
begin: 0,
|
||||||
begin: 0,
|
end: _playback.durationCurrent.value.inMilliseconds /
|
||||||
end: _playback.durationCurrent.value.inMilliseconds /
|
max(_playback.durationTotal.value.inMilliseconds, 1),
|
||||||
max(_playback.durationTotal.value.inMilliseconds, 1),
|
|
||||||
),
|
|
||||||
duration: const Duration(milliseconds: 1000),
|
|
||||||
builder: (context, value, _) => LinearProgressIndicator(
|
|
||||||
minHeight: 3,
|
|
||||||
value: value,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
duration: const Duration(milliseconds: 1000),
|
||||||
|
builder: (context, value, _) => LinearProgressIndicator(
|
||||||
|
minHeight: 3,
|
||||||
|
value: _isFetchingActiveTrack ? null : value,
|
||||||
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
@ -12,6 +12,7 @@ import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
|||||||
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/models/video_info.dart';
|
import 'package:rhythm_box/services/sourced_track/models/video_info.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sources/netease.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sources/piped.dart';
|
import 'package:rhythm_box/services/sourced_track/sources/piped.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sources/youtube.dart';
|
import 'package:rhythm_box/services/sourced_track/sources/youtube.dart';
|
||||||
import 'package:rhythm_box/services/artist.dart';
|
import 'package:rhythm_box/services/artist.dart';
|
||||||
@ -42,6 +43,7 @@ class _SiblingTracksState extends State<SiblingTracks> {
|
|||||||
final sourceInfoToLabelMap = {
|
final sourceInfoToLabelMap = {
|
||||||
YoutubeSourceInfo: 'YouTube',
|
YoutubeSourceInfo: 'YouTube',
|
||||||
PipedSourceInfo: 'Piped',
|
PipedSourceInfo: 'Piped',
|
||||||
|
NeteaseSourceInfo: 'Netease',
|
||||||
};
|
};
|
||||||
|
|
||||||
List<StreamSubscription>? _subscriptions;
|
List<StreamSubscription>? _subscriptions;
|
||||||
@ -107,6 +109,25 @@ class _SiblingTracksState extends State<SiblingTracks> {
|
|||||||
),
|
),
|
||||||
growable: true,
|
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 activeSourceInfo = (_activeTrack! as SourcedTrack).sourceInfo;
|
||||||
|
_siblings = List.from(
|
||||||
|
searchResults
|
||||||
|
..removeWhere((element) => element.id == activeSourceInfo.id)
|
||||||
|
..insert(
|
||||||
|
0,
|
||||||
|
activeSourceInfo,
|
||||||
|
),
|
||||||
|
growable: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() => _isSearching = false);
|
setState(() => _isSearching = false);
|
||||||
|
74
lib/widgets/player/track_source_details.dart
Normal file
74
lib/widgets/player/track_source_details.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rhythm_box/services/duration.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sources/netease.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sources/piped.dart';
|
||||||
|
import 'package:rhythm_box/services/sourced_track/sources/youtube.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
class TrackSourceDetails extends StatelessWidget {
|
||||||
|
final Track track;
|
||||||
|
|
||||||
|
const TrackSourceDetails({super.key, required this.track});
|
||||||
|
|
||||||
|
static final sourceInfoToLabelMap = {
|
||||||
|
YoutubeSourceInfo: 'YouTube',
|
||||||
|
PipedSourceInfo: 'Piped',
|
||||||
|
NeteaseSourceInfo: 'Netease',
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
final detailsMap = {
|
||||||
|
'Title': track.name!,
|
||||||
|
'Artist': track.artists?.map((x) => x.name).join(', '),
|
||||||
|
'Album': track.album!.name!,
|
||||||
|
'Duration': (track is SourcedTrack
|
||||||
|
? (track as SourcedTrack).sourceInfo.duration
|
||||||
|
: track.duration!)
|
||||||
|
.toHumanReadableString(),
|
||||||
|
if (track.album!.releaseDate != null)
|
||||||
|
'Released': track.album!.releaseDate,
|
||||||
|
'Popularity': track.popularity?.toString() ?? '0',
|
||||||
|
'Provider': sourceInfoToLabelMap[track.runtimeType],
|
||||||
|
};
|
||||||
|
|
||||||
|
return Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: FixedColumnWidth(95),
|
||||||
|
1: FixedColumnWidth(10),
|
||||||
|
2: FlexColumnWidth(1),
|
||||||
|
},
|
||||||
|
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||||
|
children: [
|
||||||
|
for (final entry in detailsMap.entries)
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
TableCell(
|
||||||
|
verticalAlignment: TableCellVerticalAlignment.top,
|
||||||
|
child: Text(
|
||||||
|
entry.key,
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const TableCell(
|
||||||
|
verticalAlignment: TableCellVerticalAlignment.top,
|
||||||
|
child: Text(':'),
|
||||||
|
),
|
||||||
|
if (entry.value is Widget)
|
||||||
|
entry.value as Widget
|
||||||
|
else if (entry.value is String)
|
||||||
|
Text(
|
||||||
|
entry.value as String,
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Text('Unknown'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ import shared_preferences_foundation
|
|||||||
import sqflite
|
import sqflite
|
||||||
import sqlite3_flutter_libs
|
import sqlite3_flutter_libs
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
import wakelock_plus
|
||||||
import window_manager
|
import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
@ -36,5 +37,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
|
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
|
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,8 @@ PODS:
|
|||||||
- sqlite3/rtree
|
- sqlite3/rtree
|
||||||
- url_launcher_macos (0.0.1):
|
- url_launcher_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- wakelock_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- window_manager (0.2.0):
|
- window_manager (0.2.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
||||||
@ -74,6 +76,7 @@ DEPENDENCIES:
|
|||||||
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
|
||||||
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`)
|
- sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
|
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
||||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@ -116,6 +119,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
|
wakelock_plus:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
||||||
window_manager:
|
window_manager:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||||
|
|
||||||
@ -139,6 +144,7 @@ SPEC CHECKSUMS:
|
|||||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||||
sqlite3_flutter_libs: 5ca46c1a04eddfbeeb5b16566164aa7ad1616e7b
|
sqlite3_flutter_libs: 5ca46c1a04eddfbeeb5b16566164aa7ad1616e7b
|
||||||
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
|
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
|
||||||
|
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
|
||||||
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
||||||
|
|
||||||
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||||
|
@ -269,7 +269,6 @@
|
|||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
CreatedOnToolsVersion = 9.2;
|
CreatedOnToolsVersion = 9.2;
|
||||||
LastSwiftMigration = 1100;
|
LastSwiftMigration = 1100;
|
||||||
ProvisioningStyle = Automatic;
|
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.Sandbox = {
|
com.apple.Sandbox = {
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
@ -477,6 +476,7 @@
|
|||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -492,6 +492,7 @@
|
|||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -507,6 +508,7 @@
|
|||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -571,7 +573,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = W7HPZ53V6B;
|
DEVELOPMENT_TEAM = W7HPZ53V6B;
|
||||||
@ -582,6 +584,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
@ -591,6 +594,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
@ -707,7 +711,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = W7HPZ53V6B;
|
DEVELOPMENT_TEAM = W7HPZ53V6B;
|
||||||
@ -718,6 +722,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@ -731,7 +736,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = W7HPZ53V6B;
|
DEVELOPMENT_TEAM = W7HPZ53V6B;
|
||||||
@ -742,6 +747,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
@ -751,6 +757,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@ -759,6 +766,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "rhythm_box.app"
|
BuildableName = "GroovyBox.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "rhythm_box.app"
|
BuildableName = "GroovyBox.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@ -65,7 +65,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "rhythm_box.app"
|
BuildableName = "GroovyBox.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "rhythm_box.app"
|
BuildableName = "GroovyBox.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// 'flutter create' template.
|
// 'flutter create' template.
|
||||||
|
|
||||||
// The application's name. By default this is also the title of the Flutter window.
|
// The application's name. By default this is also the title of the Flutter window.
|
||||||
PRODUCT_NAME = rhythm_box
|
PRODUCT_NAME = GroovyBox
|
||||||
|
|
||||||
// The application's bundle identifier
|
// The application's bundle identifier
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox
|
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.rhythmBox
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.aps-environment</key>
|
||||||
|
<string>development</string>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.aps-environment</key>
|
||||||
|
<string>development</string>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.device.bluetooth</key>
|
<key>com.apple.security.device.bluetooth</key>
|
||||||
|
40
pubspec.lock
40
pubspec.lock
@ -310,6 +310,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.6"
|
version: "2.3.6"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.10"
|
||||||
desktop_webview_window:
|
desktop_webview_window:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -339,10 +347,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
|
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.6.0"
|
version: "5.7.0"
|
||||||
dio_web_adapter:
|
dio_web_adapter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -375,6 +383,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.19.1"
|
version: "2.19.1"
|
||||||
|
dropdown_button2:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dropdown_button2
|
||||||
|
sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.9"
|
||||||
duration:
|
duration:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1506,10 +1522,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
|
sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.2"
|
version: "4.5.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1526,6 +1542,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.5"
|
version: "14.2.5"
|
||||||
|
wakelock_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: wakelock_plus
|
||||||
|
sha256: bf4ee6f17a2fa373ed3753ad0e602b7603f8c75af006d5b9bdade263928c0484
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.8"
|
||||||
|
wakelock_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock_plus_platform_interface
|
||||||
|
sha256: "422d1cdbb448079a8a62a5a770b69baa489f8f7ca21aef47800c726d404f9d16"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
13
pubspec.yaml
13
pubspec.yaml
@ -1,8 +1,8 @@
|
|||||||
name: rhythm_box
|
name: rhythm_box
|
||||||
description: "A new Flutter project."
|
description: Yet another Spotify third-party client.
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
# The following defines the version and build number for your application.
|
# The following defines the version and build number for your application.
|
||||||
# A version number is three numbers separated by dots, like 1.2.43
|
# A version number is three numbers separated by dots, like 1.2.43
|
||||||
@ -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
|
# 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
|
# 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.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.0+3
|
version: 1.0.0+8
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.0
|
sdk: ^3.5.0
|
||||||
@ -31,7 +31,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
@ -101,6 +100,8 @@ dependencies:
|
|||||||
flutter_inappwebview: ^6.0.0
|
flutter_inappwebview: ^6.0.0
|
||||||
timezone: ^0.9.4
|
timezone: ^0.9.4
|
||||||
url_launcher: ^6.3.0
|
url_launcher: ^6.3.0
|
||||||
|
wakelock_plus: ^1.2.8
|
||||||
|
dropdown_button2: ^2.3.9
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -124,7 +125,6 @@ dev_dependencies:
|
|||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
@ -179,4 +179,5 @@ flutter_launcher_icons:
|
|||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
color: "#fef8f5"
|
color: "#fef8f5"
|
||||||
color_dark: "#18120d"
|
color_dark: "#18120d"
|
||||||
image: assets/icon-w-shadow.png
|
image: assets/icon-w-shadow.png
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
|
|
||||||
<title>RhythmBox</title>
|
<title>RhythmBox</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
|
|
||||||
|
|
||||||
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||||
<style id="splash-screen-style">
|
<style id="splash-screen-style">
|
||||||
html {
|
html {
|
||||||
height: 100%
|
height: 100%
|
||||||
@ -100,7 +103,6 @@
|
|||||||
document.body.style.background = "transparent";
|
document.body.style.background = "transparent";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<picture id="splash">
|
<picture id="splash">
|
||||||
@ -108,6 +110,7 @@
|
|||||||
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
|
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
|
||||||
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
|
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
<script src="flutter_bootstrap.js" async=""></script>
|
<script src="flutter_bootstrap.js" async=""></script>
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user