Netease cloud music login

This commit is contained in:
2024-09-06 12:41:35 +08:00
parent 6cdc025c40
commit 6509cd2511
10 changed files with 602 additions and 170 deletions

View File

@ -0,0 +1,156 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import 'package:rhythm_box/providers/auth.dart';
import 'package:rhythm_box/providers/error_notifier.dart';
import 'package:rhythm_box/services/sourced_track/sources/netease.dart';
import 'package:rhythm_box/widgets/sized_container.dart';
class LoginNeteaseScreen extends StatefulWidget {
const LoginNeteaseScreen({super.key});
@override
State<LoginNeteaseScreen> createState() => _LoginNeteaseScreenState();
}
class _LoginNeteaseScreenState extends State<LoginNeteaseScreen> {
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _phoneRegionController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
late final AuthenticationProvider _auth = Get.find();
bool _isLogging = false;
Future<void> _sentCaptcha() async {
setState(() => _isLogging = true);
final phone = _phoneController.text;
var region = _phoneRegionController.text;
if (region.isEmpty) region = '86';
final client = NeteaseSourcedTrack.getClient();
final resp = await client.get(
'/captcha/sent?phone=$phone&ctcode=$region&timestamp=${DateTime.now().millisecondsSinceEpoch}',
);
if (resp.statusCode != 200 || resp.body?['code'] != 200) {
Get.find<ErrorNotifier>().showError(
resp.bodyString ?? resp.status.toString(),
);
}
setState(() => _isLogging = false);
}
Future<void> _performLogin() async {
setState(() => _isLogging = true);
final phone = _phoneController.text;
final password = _passwordController.text;
var region = _phoneRegionController.text;
if (region.isEmpty) region = '86';
final client = NeteaseSourcedTrack.getClient();
final resp = await client.get(
'/login/cellphone?phone=$phone&captcha=$password&countrycode=$region&timestamp=${DateTime.now().millisecondsSinceEpoch}',
);
if (resp.statusCode != 200 || resp.body?['code'] != 200) {
Get.find<ErrorNotifier>().showError(
resp.bodyString ?? resp.status.toString(),
);
setState(() => _isLogging = false);
return;
}
await _auth.setNeteaseCredentials(resp.body['cookie']);
setState(() => _isLogging = false);
GoRouter.of(context).goNamed('settings');
}
@override
void dispose() {
_phoneController.dispose();
_phoneRegionController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Connect with Netease Cloud Music'),
),
body: CenteredContainer(
maxWidth: 320,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
SizedBox(
width: 64,
child: TextField(
controller: _phoneRegionController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: '86',
isDense: true,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Gap(8),
Expanded(
child: TextField(
controller: _phoneController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text('Phone Number'),
isDense: true,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
),
],
),
const Gap(8),
Row(
children: [
Expanded(
child: TextField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text('Captcha Code'),
isDense: true,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
),
const Gap(8),
IconButton(
onPressed: _isLogging ? null : () => _sentCaptcha(),
icon: const Icon(Icons.sms),
tooltip: 'Get Captcha',
),
],
),
const Gap(8),
TextButton(
onPressed: _isLogging ? null : () => _performLogin(),
child: const Text('Login'),
)
],
),
),
);
}
}

View File

@ -72,8 +72,8 @@ class _ExploreScreenState extends State<ExploreScreen> {
}
if (_auth.auth.value != null) {
final customEndpoint =
CustomSpotifyEndpoints(_auth.auth.value?.accessToken.value ?? '');
final customEndpoint = CustomSpotifyEndpoints(
_auth.auth.value?.spotifyAccessToken.value ?? '');
final forYouView = await customEndpoint.getView(
'made-for-x-hub',
market: market,

View File

@ -7,6 +7,7 @@ import 'package:rhythm_box/providers/spotify.dart';
import 'package:rhythm_box/providers/user_preferences.dart';
import 'package:rhythm_box/screens/auth/login.dart';
import 'package:rhythm_box/services/database/database.dart';
import 'package:rhythm_box/services/sourced_track/sources/netease.dart';
import 'package:rhythm_box/widgets/auto_cache_image.dart';
import 'package:rhythm_box/widgets/sized_container.dart';
@ -86,6 +87,75 @@ class _SettingsScreenState extends State<SettingsScreen> {
},
);
}),
Obx(() {
if (_authenticate.auth.value == null) {
return const SizedBox();
}
if (_authenticate.auth.value?.neteaseCookie == null) {
return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Icons.cloud_outlined),
title: const Text('Connect with Netease Cloud Music'),
subtitle: const Text(
'Make us able to play more music from Netease Cloud Music'),
trailing: const Icon(Icons.chevron_right),
enabled: !_isLoggingIn,
onTap: () async {
setState(() => _isLoggingIn = true);
await GoRouter.of(context)
.pushNamed('authMobileLoginNetease');
setState(() => _isLoggingIn = false);
},
);
}
return FutureBuilder(
future: NeteaseSourcedTrack.getClient().get('/user/account'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 24),
leading: const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 3,
),
),
title: const Text('Loading...'),
trailing: IconButton(
onPressed: () {
_authenticate.logoutNetease();
},
icon: const Icon(Icons.link_off),
),
);
}
return ListTile(
leading:
snapshot.data!.body['profile']['avatarUrl'] != null
? CircleAvatar(
backgroundImage: AutoCacheImage.provider(
snapshot.data!.body['profile']['avatarUrl'],
),
)
: const Icon(Icons.account_circle),
title: Text(snapshot.data!.body['profile']['nickname']),
subtitle: const Text(
'Connected with your Netease Cloud Music account',
),
trailing: IconButton(
onPressed: () {
_authenticate.logoutNetease();
},
icon: const Icon(Icons.link_off),
),
);
},
);
}),
Obx(() {
if (_authenticate.auth.value == null) {
return const SizedBox();
@ -95,7 +165,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Icons.logout),
title: const Text('Log out'),
subtitle: const Text('Disconnect with this Spotify account'),
subtitle: const Text('Disconnect with every account'),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
_authenticate.logout();