✨ Netease backend support
This commit is contained in:
parent
010ee6286f
commit
19a7fd82df
@ -8,10 +8,10 @@ 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
|
||||||
- [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" />
|
||||||
|
@ -7,7 +7,7 @@ class ErrorNotifier extends GetxController {
|
|||||||
Rx<MaterialBanner?> showing = Rx(null);
|
Rx<MaterialBanner?> showing = Rx(null);
|
||||||
|
|
||||||
void logError(String msg, {StackTrace? trace}) {
|
void logError(String msg, {StackTrace? trace}) {
|
||||||
log('$msg${trace != null ? '\nTrace:\ntrace' : ''}');
|
log('$msg${trace != null ? '\nTrace:\n$trace' : ''}');
|
||||||
showError(msg);
|
showError(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,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)));
|
||||||
}
|
}
|
||||||
@ -161,10 +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 setNormalizeAudio(bool normalize) {
|
void setNormalizeAudio(bool normalize) {
|
||||||
setData(PreferencesTableCompanion(normalizeAudio: Value(normalize)));
|
setData(PreferencesTableCompanion(normalizeAudio: Value(normalize)));
|
||||||
audioPlayer.setAudioNormalization(normalize);
|
audioPlayer.setAudioNormalization(normalize);
|
||||||
|
@ -71,6 +71,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_auth.auth.value != null) {
|
||||||
final customEndpoint =
|
final customEndpoint =
|
||||||
CustomSpotifyEndpoints(_auth.auth.value?.accessToken.value ?? '');
|
CustomSpotifyEndpoints(_auth.auth.value?.accessToken.value ?? '');
|
||||||
final forYouView = await customEndpoint.getView(
|
final forYouView = await customEndpoint.getView(
|
||||||
@ -79,6 +80,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
locale: Intl.canonicalizedLocale(locale.toString()),
|
locale: Intl.canonicalizedLocale(locale.toString()),
|
||||||
);
|
);
|
||||||
_forYouView = forYouView['content']?['items'];
|
_forYouView = forYouView['content']?['items'];
|
||||||
|
}
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _isLoading['forYou'] = false);
|
setState(() => _isLoading['forYou'] = false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -69,9 +69,10 @@ 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: Center(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -103,7 +104,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Icon(Icons.image)),
|
child: Icon(Icons.image),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).marginSymmetric(horizontal: 24),
|
).marginSymmetric(horizontal: 24),
|
||||||
@ -122,14 +124,16 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
Text(
|
Text(
|
||||||
_playback.state.value.activeTrack?.name ??
|
_playback.state.value.activeTrack?.name ??
|
||||||
'Not playing',
|
'Not playing',
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style:
|
||||||
|
Theme.of(context).textTheme.titleLarge,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_playback.state.value.activeTrack?.artists
|
_playback.state.value.activeTrack?.artists
|
||||||
?.asString() ??
|
?.asString() ??
|
||||||
'No author',
|
'No author',
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style:
|
||||||
|
Theme.of(context).textTheme.bodyMedium,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
@ -166,7 +170,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
.abs()
|
.abs()
|
||||||
.toDouble(),
|
.toDouble(),
|
||||||
value: _draggingValue?.abs() ??
|
value: _draggingValue?.abs() ??
|
||||||
_playback.durationCurrent.value.inMilliseconds
|
_playback
|
||||||
|
.durationCurrent.value.inMilliseconds
|
||||||
.toDouble()
|
.toDouble()
|
||||||
.abs(),
|
.abs(),
|
||||||
min: 0,
|
min: 0,
|
||||||
@ -293,7 +298,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
PlaylistMode.single,
|
PlaylistMode.single,
|
||||||
PlaylistMode.single =>
|
PlaylistMode.single =>
|
||||||
PlaylistMode.none,
|
PlaylistMode.none,
|
||||||
PlaylistMode.none => PlaylistMode.loop,
|
PlaylistMode.none =>
|
||||||
|
PlaylistMode.loop,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -313,7 +319,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const PlayerQueuePopup(),
|
builder: (context) =>
|
||||||
|
const PlayerQueuePopup(),
|
||||||
).then((_) {
|
).then((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@ -329,7 +336,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
icon: const Icon(Icons.lyrics),
|
icon: const Icon(Icons.lyrics),
|
||||||
label: const Text('Lyrics'),
|
label: const Text('Lyrics'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
GoRouter.of(context).pushNamed('playerLyrics');
|
GoRouter.of(context)
|
||||||
|
.pushNamed('playerLyrics');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -365,6 +373,7 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
).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';
|
||||||
|
|
||||||
@ -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),
|
||||||
|
@ -94,7 +94,7 @@ abstract class AudioPlayerInterface {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_mkPlayer.stream.error.listen((event) {
|
_mkPlayer.stream.error.listen((event) {
|
||||||
Get.find<ErrorNotifier>().logError('[Playback] Error: $event');
|
Get.find<ErrorNotifier>().logError('[Playback][Player] Error: $event');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,8 @@ class CustomPlayer extends Player {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
stream.error.listen((event) {
|
stream.error.listen((event) {
|
||||||
Get.find<ErrorNotifier>().logError('[Playback] Error: $event');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Playback][CustomLayer] Error: $event');
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
PackageInfo.fromPlatform().then((packageInfo) {
|
PackageInfo.fromPlatform().then((packageInfo) {
|
||||||
|
@ -453,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
|
||||||
@ -494,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
|
||||||
@ -543,11 +542,11 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
downloadLocation,
|
downloadLocation,
|
||||||
localLibraryLocation,
|
localLibraryLocation,
|
||||||
pipedInstance,
|
pipedInstance,
|
||||||
|
neteaseApiInstance,
|
||||||
themeMode,
|
themeMode,
|
||||||
audioSource,
|
audioSource,
|
||||||
streamMusicCodec,
|
streamMusicCodec,
|
||||||
downloadMusicCodec,
|
downloadMusicCodec,
|
||||||
discordPresence,
|
|
||||||
endlessPlayback,
|
endlessPlayback,
|
||||||
playerWakelock
|
playerWakelock
|
||||||
];
|
];
|
||||||
@ -622,16 +621,16 @@ 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,
|
||||||
@ -696,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'])!),
|
||||||
@ -708,8 +709,6 @@ 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'])!,
|
||||||
playerWakelock: attachedDatabase.typeMapping
|
playerWakelock: attachedDatabase.typeMapping
|
||||||
@ -771,11 +770,11 @@ 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 playerWakelock;
|
final bool playerWakelock;
|
||||||
const PreferencesTableData(
|
const PreferencesTableData(
|
||||||
@ -796,11 +795,11 @@ 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.playerWakelock});
|
required this.playerWakelock});
|
||||||
@override
|
@override
|
||||||
@ -849,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));
|
||||||
@ -867,7 +867,6 @@ 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['player_wakelock'] = Variable<bool>(playerWakelock);
|
map['player_wakelock'] = Variable<bool>(playerWakelock);
|
||||||
return map;
|
return map;
|
||||||
@ -892,11 +891,11 @@ 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),
|
||||||
playerWakelock: Value(playerWakelock),
|
playerWakelock: Value(playerWakelock),
|
||||||
);
|
);
|
||||||
@ -930,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
|
||||||
@ -938,7 +939,6 @@ 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']),
|
||||||
playerWakelock: serializer.fromJson<bool>(json['playerWakelock']),
|
playerWakelock: serializer.fromJson<bool>(json['playerWakelock']),
|
||||||
);
|
);
|
||||||
@ -970,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>(
|
||||||
@ -980,7 +981,6 @@ 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),
|
||||||
'playerWakelock': serializer.toJson<bool>(playerWakelock),
|
'playerWakelock': serializer.toJson<bool>(playerWakelock),
|
||||||
};
|
};
|
||||||
@ -1004,11 +1004,11 @@ 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? playerWakelock}) =>
|
bool? playerWakelock}) =>
|
||||||
PreferencesTableData(
|
PreferencesTableData(
|
||||||
@ -1029,11 +1029,11 @@ 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,
|
||||||
playerWakelock: playerWakelock ?? this.playerWakelock,
|
playerWakelock: playerWakelock ?? this.playerWakelock,
|
||||||
);
|
);
|
||||||
@ -1081,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,
|
||||||
@ -1090,9 +1093,6 @@ 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,
|
||||||
@ -1122,11 +1122,11 @@ 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('playerWakelock: $playerWakelock')
|
..write('playerWakelock: $playerWakelock')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
@ -1152,11 +1152,11 @@ class PreferencesTableData extends DataClass
|
|||||||
downloadLocation,
|
downloadLocation,
|
||||||
localLibraryLocation,
|
localLibraryLocation,
|
||||||
pipedInstance,
|
pipedInstance,
|
||||||
|
neteaseApiInstance,
|
||||||
themeMode,
|
themeMode,
|
||||||
audioSource,
|
audioSource,
|
||||||
streamMusicCodec,
|
streamMusicCodec,
|
||||||
downloadMusicCodec,
|
downloadMusicCodec,
|
||||||
discordPresence,
|
|
||||||
endlessPlayback,
|
endlessPlayback,
|
||||||
playerWakelock
|
playerWakelock
|
||||||
]);
|
]);
|
||||||
@ -1181,11 +1181,11 @@ 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.playerWakelock == this.playerWakelock);
|
other.playerWakelock == this.playerWakelock);
|
||||||
}
|
}
|
||||||
@ -1208,11 +1208,11 @@ 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> playerWakelock;
|
final Value<bool> playerWakelock;
|
||||||
const PreferencesTableCompanion({
|
const PreferencesTableCompanion({
|
||||||
@ -1233,11 +1233,11 @@ 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.playerWakelock = const Value.absent(),
|
this.playerWakelock = const Value.absent(),
|
||||||
});
|
});
|
||||||
@ -1259,11 +1259,11 @@ 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.playerWakelock = const Value.absent(),
|
this.playerWakelock = const Value.absent(),
|
||||||
});
|
});
|
||||||
@ -1285,11 +1285,11 @@ 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>? playerWakelock,
|
Expression<bool>? playerWakelock,
|
||||||
}) {
|
}) {
|
||||||
@ -1313,12 +1313,13 @@ 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 (playerWakelock != null) 'player_wakelock': playerWakelock,
|
if (playerWakelock != null) 'player_wakelock': playerWakelock,
|
||||||
});
|
});
|
||||||
@ -1342,11 +1343,11 @@ 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>? playerWakelock}) {
|
Value<bool>? playerWakelock}) {
|
||||||
return PreferencesTableCompanion(
|
return PreferencesTableCompanion(
|
||||||
@ -1367,11 +1368,11 @@ 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,
|
||||||
playerWakelock: playerWakelock ?? this.playerWakelock,
|
playerWakelock: playerWakelock ?? this.playerWakelock,
|
||||||
);
|
);
|
||||||
@ -1443,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));
|
||||||
@ -1462,9 +1466,6 @@ 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);
|
||||||
}
|
}
|
||||||
@ -1494,11 +1495,11 @@ 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('playerWakelock: $playerWakelock')
|
..write('playerWakelock: $playerWakelock')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
@ -3950,11 +3951,11 @@ 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> playerWakelock,
|
Value<bool> playerWakelock,
|
||||||
});
|
});
|
||||||
@ -3977,11 +3978,11 @@ 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> playerWakelock,
|
Value<bool> playerWakelock,
|
||||||
});
|
});
|
||||||
@ -4021,11 +4022,11 @@ 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> playerWakelock = const Value.absent(),
|
Value<bool> playerWakelock = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
@ -4047,11 +4048,11 @@ 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,
|
||||||
playerWakelock: playerWakelock,
|
playerWakelock: playerWakelock,
|
||||||
),
|
),
|
||||||
@ -4073,11 +4074,11 @@ 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> playerWakelock = const Value.absent(),
|
Value<bool> playerWakelock = const Value.absent(),
|
||||||
}) =>
|
}) =>
|
||||||
@ -4099,11 +4100,11 @@ 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,
|
||||||
playerWakelock: playerWakelock,
|
playerWakelock: playerWakelock,
|
||||||
),
|
),
|
||||||
@ -4214,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,
|
||||||
@ -4242,11 +4248,6 @@ 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) =>
|
||||||
@ -4346,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) =>
|
||||||
@ -4366,11 +4372,6 @@ 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) =>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
@ -74,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 =>
|
||||||
@ -82,8 +85,6 @@ 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 playerWakelock =>
|
BoolColumn get playerWakelock =>
|
||||||
@ -108,12 +109,12 @@ 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,
|
||||||
playerWakelock: true,
|
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;
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ abstract class AudioPlayerInterface {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_mkPlayer.stream.error.listen((event) {
|
_mkPlayer.stream.error.listen((event) {
|
||||||
Get.find<ErrorNotifier>().logError('[Playback] Error: $event');
|
Get.find<ErrorNotifier>().logError('[Playback][Media] Error: $event');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ 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 {
|
||||||
@ -24,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',
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -85,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(
|
||||||
@ -140,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);
|
||||||
@ -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'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
16
pubspec.lock
16
pubspec.lock
@ -347,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:
|
||||||
@ -383,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:
|
||||||
@ -1514,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:
|
||||||
|
@ -101,6 +101,7 @@ dependencies:
|
|||||||
timezone: ^0.9.4
|
timezone: ^0.9.4
|
||||||
url_launcher: ^6.3.0
|
url_launcher: ^6.3.0
|
||||||
wakelock_plus: ^1.2.8
|
wakelock_plus: ^1.2.8
|
||||||
|
dropdown_button2: ^2.3.9
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user