You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
9.0 KiB
326 lines
9.0 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:qq_music/qq_music.dart';
|
|
import 'package:voitrans/data/services/qq_musoc.dart';
|
|
import '../models/song_model.dart';
|
|
|
|
class QQMusicController extends GetxController {
|
|
final QqMusocService _qqMusic = Get.find<QqMusocService>();
|
|
|
|
// 登录状态
|
|
final isLoggedIn = false.obs;
|
|
final userInfo = Rxn<QQMusicLoginResult>();
|
|
|
|
// 搜索相关
|
|
final searchResults = <SongModel>[].obs;
|
|
final isSearching = false.obs;
|
|
final searchKeyword = ''.obs;
|
|
|
|
// 播放相关
|
|
final currentSong = Rxn<SongModel>();
|
|
final isPlaying = false.obs;
|
|
final currentPosition = 0.obs;
|
|
final totalDuration = 0.obs;
|
|
final playlist = <SongModel>[].obs;
|
|
final currentIndex = 0.obs;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_checkLoginStatus();
|
|
_startPlaybackStateMonitor();
|
|
}
|
|
|
|
/// 检查登录状态
|
|
void _checkLoginStatus() {
|
|
// 这里可以检查本地存储的登录信息
|
|
// 暂时设为未登录状态
|
|
isLoggedIn.value = false;
|
|
}
|
|
|
|
/// QQ音乐登录
|
|
Future<void> login() async {
|
|
try {
|
|
print('开始QQ音乐登录...');
|
|
|
|
Get.dialog(
|
|
const Center(
|
|
child: CircularProgressIndicator(),
|
|
),
|
|
barrierDismissible: false,
|
|
);
|
|
|
|
// 添加超时处理
|
|
final result = await _qqMusic.login().timeout(
|
|
const Duration(seconds: 30),
|
|
onTimeout: () {
|
|
print('QQ音乐登录超时');
|
|
return {'success': false, 'error': '登录超时,请重试'};
|
|
},
|
|
);
|
|
|
|
print('QQ音乐登录结果: $result');
|
|
|
|
Get.back(); // 关闭加载对话框
|
|
|
|
if (result['success'] == true) {
|
|
userInfo.value = QQMusicLoginResult.fromMap(result);
|
|
isLoggedIn.value = true;
|
|
print('QQ音乐登录成功: ${result['openId']}');
|
|
Get.snackbar('登录成功', '欢迎使用QQ音乐');
|
|
} else {
|
|
print('QQ音乐登录失败: ${result['error']}');
|
|
Get.snackbar('登录失败', result['error'] ?? '未知错误');
|
|
}
|
|
} catch (e) {
|
|
print('QQ音乐登录异常: $e');
|
|
Get.back();
|
|
Get.snackbar('登录失败', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 退出登录
|
|
void logout() {
|
|
userInfo.value = null;
|
|
isLoggedIn.value = false;
|
|
currentSong.value = null;
|
|
isPlaying.value = false;
|
|
playlist.clear();
|
|
Get.snackbar('已退出', '已退出QQ音乐登录');
|
|
}
|
|
|
|
/// 搜索歌曲
|
|
Future<void> searchSongs(String keyword) async {
|
|
if (keyword.trim().isEmpty) return;
|
|
|
|
print('开始搜索歌曲: $keyword');
|
|
searchKeyword.value = keyword;
|
|
isSearching.value = true;
|
|
|
|
try {
|
|
// 检查登录状态
|
|
if (!isLoggedIn.value) {
|
|
print('用户未登录,无法搜索');
|
|
Get.snackbar('搜索失败', '请先登录QQ音乐');
|
|
return;
|
|
}
|
|
|
|
print('调用搜索接口...');
|
|
final result = await _qqMusic.searchSongs(keyword: keyword).timeout(
|
|
const Duration(seconds: 15),
|
|
onTimeout: () {
|
|
print('搜索超时');
|
|
return {'success': false, 'error': '搜索超时,请重试'};
|
|
},
|
|
);
|
|
|
|
print('搜索结果: $result');
|
|
|
|
if (result['success'] == true && result['songs'] != null) {
|
|
final List<dynamic> songsData = result['songs'];
|
|
print('找到 ${songsData.length} 首歌曲');
|
|
|
|
final songs = songsData.map((songData) {
|
|
return SongModel(
|
|
mid: songData['mid'] ?? '',
|
|
name: songData['name'] ?? '',
|
|
artist: songData['singer'] ?? '',
|
|
album: songData['album'] ?? '',
|
|
albumPic: songData['pic'],
|
|
duration: songData['duration'] ?? 0,
|
|
);
|
|
}).toList();
|
|
|
|
searchResults.assignAll(songs);
|
|
print('搜索成功,更新UI');
|
|
} else {
|
|
print('搜索失败: ${result['error']}');
|
|
Get.snackbar('搜索失败', result['error'] ?? '搜索结果为空');
|
|
searchResults.clear();
|
|
}
|
|
} catch (e) {
|
|
print('搜索异常: $e');
|
|
Get.snackbar('搜索失败', e.toString());
|
|
searchResults.clear();
|
|
} finally {
|
|
print('搜索完成,重置状态');
|
|
isSearching.value = false;
|
|
}
|
|
}
|
|
|
|
/// 播放歌曲
|
|
Future<void> playSong(SongModel song, {List<SongModel>? songList}) async {
|
|
try {
|
|
currentSong.value = song;
|
|
|
|
if (songList != null) {
|
|
playlist.assignAll(songList);
|
|
currentIndex.value = songList.indexOf(song);
|
|
} else {
|
|
playlist.assignAll([song]);
|
|
currentIndex.value = 0;
|
|
}
|
|
|
|
// 获取歌曲MID列表
|
|
final songMids = playlist.map((s) => s.mid).toList();
|
|
final playIndex = currentIndex.value;
|
|
|
|
// 调用QQ音乐播放接口
|
|
final result = await _qqMusic.playSongs(
|
|
songMids: songMids,
|
|
index: playIndex,
|
|
);
|
|
|
|
if (result['success'] == true) {
|
|
isPlaying.value = true;
|
|
totalDuration.value = song.duration;
|
|
currentPosition.value = 0;
|
|
Get.snackbar('开始播放', song.name);
|
|
} else {
|
|
Get.snackbar('播放失败', result['error'] ?? '未知错误');
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('播放失败', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 暂停/继续播放
|
|
Future<void> togglePlayPause() async {
|
|
try {
|
|
if (isPlaying.value) {
|
|
final result = await _qqMusic.pauseMusic();
|
|
if (result['success'] == true) {
|
|
isPlaying.value = false;
|
|
}
|
|
} else {
|
|
final result = await _qqMusic.resumeMusic();
|
|
if (result['success'] == true) {
|
|
isPlaying.value = true;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('操作失败', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 上一首
|
|
Future<void> previousSong() async {
|
|
try {
|
|
final result = await _qqMusic.skipToPrevious();
|
|
if (result['success'] == true) {
|
|
if (playlist.isNotEmpty) {
|
|
int newIndex = currentIndex.value - 1;
|
|
if (newIndex < 0) newIndex = playlist.length - 1;
|
|
currentIndex.value = newIndex;
|
|
currentSong.value = playlist[newIndex];
|
|
}
|
|
await _updateCurrentSong();
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('切换失败', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 下一首
|
|
Future<void> nextSong() async {
|
|
try {
|
|
final result = await _qqMusic.skipToNext();
|
|
if (result['success'] == true) {
|
|
if (playlist.isNotEmpty) {
|
|
int newIndex = currentIndex.value + 1;
|
|
if (newIndex >= playlist.length) newIndex = 0;
|
|
currentIndex.value = newIndex;
|
|
currentSong.value = playlist[newIndex];
|
|
}
|
|
await _updateCurrentSong();
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('切换失败', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 停止播放
|
|
Future<void> stopMusic() async {
|
|
try {
|
|
final result = await _qqMusic.stopMusic();
|
|
if (result['success'] == true) {
|
|
isPlaying.value = false;
|
|
currentPosition.value = 0;
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar('停止失败', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 更新当前播放歌曲信息
|
|
Future<void> _updateCurrentSong() async {
|
|
try {
|
|
final result = await _qqMusic.getCurrentSong();
|
|
if (result['success'] == true && result['song'] != null) {
|
|
final songData = result['song'];
|
|
final song = SongModel(
|
|
mid: songData['mid'] ?? '',
|
|
name: songData['name'] ?? '',
|
|
artist: songData['singer'] ?? '',
|
|
album: songData['album'] ?? '',
|
|
albumPic: songData['pic'],
|
|
duration: songData['duration'] ?? 0,
|
|
);
|
|
currentSong.value = song;
|
|
}
|
|
} catch (e) {
|
|
print('更新当前歌曲信息失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 启动播放状态监控
|
|
void _startPlaybackStateMonitor() {
|
|
// 每秒检查一次播放状态和时间
|
|
Stream.periodic(const Duration(seconds: 1)).listen((_) async {
|
|
if (isLoggedIn.value) {
|
|
await _updatePlaybackState();
|
|
await _updatePlayTime();
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 更新播放状态
|
|
Future<void> _updatePlaybackState() async {
|
|
try {
|
|
final result = await _qqMusic.getPlaybackState();
|
|
if (result['success'] == true && result['state'] != null) {
|
|
final state = result['state'];
|
|
// QQ音乐播放状态:0-停止,1-播放,2-暂停
|
|
isPlaying.value = state == 1;
|
|
}
|
|
} catch (e) {
|
|
// 静默处理错误,避免频繁弹窗
|
|
}
|
|
}
|
|
|
|
/// 更新播放时间
|
|
Future<void> _updatePlayTime() async {
|
|
try {
|
|
final result = await _qqMusic.getPlayTime();
|
|
if (result['success'] == true) {
|
|
currentPosition.value = result['currentTime'] ?? 0;
|
|
totalDuration.value = result['totalTime'] ?? 0;
|
|
}
|
|
} catch (e) {
|
|
// 静默处理错误,避免频繁弹窗
|
|
}
|
|
}
|
|
|
|
/// 跳转到指定位置(暂时保留,QQ音乐可能不支持)
|
|
void seekTo(int position) {
|
|
currentPosition.value = position;
|
|
// QQ音乐SDK可能不支持跳转功能
|
|
Get.snackbar('提示', '当前版本不支持跳转功能');
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
// 清理资源
|
|
super.onClose();
|
|
}
|
|
}
|
|
|