|
|
|
@ -12,112 +12,123 @@ class QQMusicPlaylistController extends GetxController { |
|
|
|
final isLoggedIn = false.obs; |
|
|
|
final userInfo = Rxn<Map<String, dynamic>>(); |
|
|
|
|
|
|
|
// 直接使用全局服务的状态 - 修复返回类型 |
|
|
|
RxBool get isPlaying => _qqMusicService.isPlaying; |
|
|
|
// 界面维护的播放列表状态 |
|
|
|
RxList<SongModel> playlist = <SongModel>[].obs; |
|
|
|
RxInt currentIndex = 0.obs; |
|
|
|
|
|
|
|
// 界面显示的当前歌曲(可能与实际播放的不同) |
|
|
|
Rxn<SongModel> displayCurrentSong = Rxn<SongModel>(); |
|
|
|
|
|
|
|
// 界面播放状态(当前播放歌曲不在列表中时为未播放状态) |
|
|
|
RxBool displayIsPlaying = false.obs; |
|
|
|
|
|
|
|
// 从全局服务获取实际播放状态(仅用于同步) |
|
|
|
RxBool get actualIsPlaying => _qqMusicService.isPlaying; |
|
|
|
RxInt get currentPosition => _qqMusicService.currentPosition; |
|
|
|
RxInt get totalDuration => _qqMusicService.totalDuration; |
|
|
|
Rxn<SongModel> get currentSong => _qqMusicService.currentSong; |
|
|
|
RxList<SongModel> get playlist => _qqMusicService.playlist; |
|
|
|
RxInt get currentIndex => _qqMusicService.currentIndex; |
|
|
|
Rxn<SongModel> get actualCurrentSong => _qqMusicService.currentSong; |
|
|
|
|
|
|
|
@override |
|
|
|
void onInit() { |
|
|
|
super.onInit(); |
|
|
|
|
|
|
|
// 使用 WidgetsBinding.instance.addPostFrameCallback 延迟执行状态更新 |
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
|
_initializePlaylist(); |
|
|
|
}); |
|
|
|
|
|
|
|
_initializePlaylist(); |
|
|
|
print('🎵 [QQMusicPlaylistController] 控制器初始化完成'); |
|
|
|
} |
|
|
|
|
|
|
|
/// 初始化播放列表 - 延迟执行避免构建期间状态更新 |
|
|
|
/// 初始化播放列表 |
|
|
|
void _initializePlaylist() { |
|
|
|
// 从路由参数获取播放列表 |
|
|
|
final arguments = Get.arguments; |
|
|
|
if (arguments != null && arguments['playlist'] != null) { |
|
|
|
final List<dynamic> playlistData = arguments['playlist']; |
|
|
|
try { |
|
|
|
// 解析播放列表数据 |
|
|
|
final songList = playlistData |
|
|
|
.map((item) { |
|
|
|
try { |
|
|
|
return SongModel.fromJson(item); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('QQMusicPlaylist', '解析歌曲数据失败: $e, 数据: $item'); |
|
|
|
return null; |
|
|
|
} |
|
|
|
}) |
|
|
|
.where((song) => song != null) |
|
|
|
.cast<SongModel>() |
|
|
|
.toList(); |
|
|
|
|
|
|
|
Logger.d('QQMusicPlaylist', '成功解析歌曲数量: ${songList.length}'); |
|
|
|
|
|
|
|
// 更新播放列表 |
|
|
|
_qqMusicService.playlist.assignAll(songList); |
|
|
|
|
|
|
|
// 安全地设置当前歌曲 |
|
|
|
if (songList.isNotEmpty) { |
|
|
|
_qqMusicService.currentSong.value = songList.first; |
|
|
|
_qqMusicService.currentIndex.value = 0; |
|
|
|
Logger.d('QQMusicPlaylist', '设置当前歌曲: ${songList.first.name}'); |
|
|
|
} else { |
|
|
|
_qqMusicService.currentSong.value = null; |
|
|
|
_qqMusicService.currentIndex.value = 0; |
|
|
|
Logger.w('QQMusicPlaylist', '播放列表为空,无法设置当前歌曲'); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
Logger.e('QQMusicPlaylist', '处理播放列表时发生异常: $e'); |
|
|
|
// 确保状态一致性 |
|
|
|
_qqMusicService.currentSong.value = null; |
|
|
|
_qqMusicService.currentIndex.value = 0; |
|
|
|
} |
|
|
|
final List<SongModel> songList = arguments['playlist']; |
|
|
|
// try { |
|
|
|
// // 解析播放列表数据 |
|
|
|
// final songList = playlistData |
|
|
|
// .map((item) { |
|
|
|
// try { |
|
|
|
// return SongModel.fromJson(item); |
|
|
|
// } catch (e) { |
|
|
|
// Logger.e('QQMusicPlaylist', '解析歌曲数据失败: $e, 数据: $item'); |
|
|
|
// return null; |
|
|
|
// } |
|
|
|
// }) |
|
|
|
// .where((song) => song != null) |
|
|
|
// .cast<SongModel>() |
|
|
|
// .toList(); |
|
|
|
|
|
|
|
// Logger.d('QQMusicPlaylist', '成功解析歌曲数量: ${songList.length}'); |
|
|
|
// 更新播放列表 |
|
|
|
playlist.assignAll(songList); |
|
|
|
// 检查当前播放歌曲是否在列表中 |
|
|
|
_syncDisplayStateWithActualPlayback(); |
|
|
|
// } catch (e) { |
|
|
|
// Logger.e('QQMusicPlaylist', '处理播放列表时发生异常: $e'); |
|
|
|
// _setDefaultDisplayState(); |
|
|
|
// } |
|
|
|
} else { |
|
|
|
Logger.w('QQMusicPlaylist', '未接收到播放列表参数'); |
|
|
|
} |
|
|
|
|
|
|
|
// 确保 playlist 初始化 |
|
|
|
if (_qqMusicService.playlist.isEmpty) { |
|
|
|
Logger.w('QQMusicPlaylist', 'Playlist is empty on init'); |
|
|
|
_setDefaultDisplayState(); |
|
|
|
} |
|
|
|
|
|
|
|
// 添加状态监听器 |
|
|
|
_qqMusicService.addListener(_onMusicStateChanged); |
|
|
|
|
|
|
|
// 界面初始化时同步播放状态和进度 |
|
|
|
_syncInitialState(); |
|
|
|
_qqMusicService.syncCurrentPlayInfo(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 同步初始播放状态 - 移除 update() 调用 |
|
|
|
Future<void> _syncInitialState() async { |
|
|
|
try { |
|
|
|
Logger.d('QQMusicPlaylist', '开始同步初始播放状态'); |
|
|
|
/// 同步显示状态与实际播放状态 |
|
|
|
void _syncDisplayStateWithActualPlayback() { |
|
|
|
if (playlist.isEmpty) { |
|
|
|
_setDefaultDisplayState(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 同步当前播放信息 |
|
|
|
await _qqMusicService.syncCurrentPlayInfo(); |
|
|
|
final actualSong = actualCurrentSong.value; |
|
|
|
|
|
|
|
// 如果当前有音乐在播放,启动状态监听 |
|
|
|
if (_qqMusicService.isPlaying.value) { |
|
|
|
Logger.d('QQMusicPlaylist', '检测到音乐正在播放,启动状态监听'); |
|
|
|
_qqMusicService.startPlaybackStateMonitor(); |
|
|
|
} |
|
|
|
if (actualSong != null) { |
|
|
|
// 查找当前播放歌曲在列表中的位置 |
|
|
|
final index = playlist.indexWhere((song) => song.mid == actualSong.mid); |
|
|
|
|
|
|
|
// 移除这行,避免在构建期间调用 update() |
|
|
|
// update(); |
|
|
|
if (index != -1) { |
|
|
|
// 当前播放歌曲在列表中 |
|
|
|
currentIndex.value = index; |
|
|
|
displayCurrentSong.value = playlist[index]; |
|
|
|
displayIsPlaying.value = actualIsPlaying.value; |
|
|
|
Logger.d('QQMusicPlaylist', '当前播放歌曲在列表中,索引: $index'); |
|
|
|
} else { |
|
|
|
// 当前播放歌曲不在列表中,显示列表第一个,状态为未播放 |
|
|
|
_setDefaultDisplayState(); |
|
|
|
Logger.d('QQMusicPlaylist', '当前播放歌曲不在列表中,显示默认状态'); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 没有当前播放歌曲,显示列表第一个,状态为未播放 |
|
|
|
_setDefaultDisplayState(); |
|
|
|
Logger.d('QQMusicPlaylist', '无当前播放歌曲,显示默认状态'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Logger.d('QQMusicPlaylist', '初始状态同步完成'); |
|
|
|
} catch (e) { |
|
|
|
Logger.e('QQMusicPlaylist', '初始状态同步失败: $e'); |
|
|
|
/// 设置默认显示状态 |
|
|
|
void _setDefaultDisplayState() { |
|
|
|
if (playlist.isNotEmpty) { |
|
|
|
currentIndex.value = 0; |
|
|
|
displayCurrentSong.value = playlist.first; |
|
|
|
displayIsPlaying.value = false; // 默认为未播放状态 |
|
|
|
Logger.d('QQMusicPlaylist', '设置默认显示状态: ${playlist.first.name}'); |
|
|
|
} else { |
|
|
|
currentIndex.value = 0; |
|
|
|
displayCurrentSong.value = null; |
|
|
|
displayIsPlaying.value = false; |
|
|
|
Logger.w('QQMusicPlaylist', '播放列表为空,无法设置默认状态'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 音乐状态变化回调 - 使用 SchedulerBinding 延迟更新 |
|
|
|
/// 音乐状态变化回调 |
|
|
|
void _onMusicStateChanged() { |
|
|
|
// 使用 SchedulerBinding 确保在构建完成后再更新 |
|
|
|
SchedulerBinding.instance.addPostFrameCallback((_) { |
|
|
|
if (!isClosed) { |
|
|
|
// 重新同步显示状态 |
|
|
|
_syncDisplayStateWithActualPlayback(); |
|
|
|
update(); |
|
|
|
} |
|
|
|
}); |
|
|
|
@ -133,7 +144,7 @@ class QQMusicPlaylistController extends GetxController { |
|
|
|
/// 播放歌曲 |
|
|
|
Future<void> playSong(SongModel song, {List<SongModel>? songList}) async { |
|
|
|
try { |
|
|
|
// 如果没有传入歌曲列表,使用当前播放列表 |
|
|
|
// 使用传入的歌曲列表或当前播放列表 |
|
|
|
final List<SongModel> finalSongList = songList ?? playlist.toList(); |
|
|
|
final songMids = finalSongList.map((s) => s.mid).toList(); |
|
|
|
final playIndex = finalSongList.indexOf(song); |
|
|
|
@ -149,6 +160,7 @@ class QQMusicPlaylistController extends GetxController { |
|
|
|
); |
|
|
|
|
|
|
|
if (result['success'] == true) { |
|
|
|
// 播放成功后,如果歌曲不在当前列表中,保持列表显示状态不变 |
|
|
|
Get.snackbar('开始播放', song.name); |
|
|
|
} else { |
|
|
|
Get.snackbar('播放失败', result['error'] ?? '未知错误'); |
|
|
|
@ -156,6 +168,10 @@ class QQMusicPlaylistController extends GetxController { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 歌曲在当前列表中,更新界面状态 |
|
|
|
currentIndex.value = playIndex; |
|
|
|
displayCurrentSong.value = song; |
|
|
|
|
|
|
|
final result = await _qqMusicService.playSongs( |
|
|
|
songMids: songMids, |
|
|
|
index: playIndex, |
|
|
|
@ -164,41 +180,102 @@ class QQMusicPlaylistController extends GetxController { |
|
|
|
); |
|
|
|
|
|
|
|
if (result['success'] == true) { |
|
|
|
displayIsPlaying.value = true; |
|
|
|
_qqMusicService.syncCurrentPlayInfo(); |
|
|
|
Get.snackbar('开始播放', song.name); |
|
|
|
} else { |
|
|
|
displayIsPlaying.value = false; |
|
|
|
Get.snackbar('播放失败', result['error'] ?? '未知错误'); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
displayIsPlaying.value = false; |
|
|
|
Get.snackbar('播放失败', e.toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 播放列表中的指定歌曲 |
|
|
|
Future<void> playPlaylistSong(int index) async { |
|
|
|
if (index >= 0 && index < playlist.length) { |
|
|
|
await playSong(playlist[index]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 暂停/继续播放 |
|
|
|
Future<void> togglePlayPause() async { |
|
|
|
if (isPlaying.value) { |
|
|
|
await _qqMusicService.pauseMusic(); |
|
|
|
// 检查当前显示的歌曲是否正在播放 |
|
|
|
final displaySong = displayCurrentSong.value; |
|
|
|
final actualSong = actualCurrentSong.value; |
|
|
|
|
|
|
|
if (displaySong != null && |
|
|
|
actualSong != null && |
|
|
|
displaySong.mid == actualSong.mid) { |
|
|
|
// 显示的歌曲正在播放,执行暂停/继续 |
|
|
|
if (actualIsPlaying.value) { |
|
|
|
await _qqMusicService.pauseMusic(); |
|
|
|
displayIsPlaying.value = false; |
|
|
|
} else { |
|
|
|
await _qqMusicService.resumeMusic(); |
|
|
|
displayIsPlaying.value = true; |
|
|
|
} |
|
|
|
} else { |
|
|
|
await _qqMusicService.resumeMusic(); |
|
|
|
// 显示的歌曲不是当前播放的,播放显示的歌曲 |
|
|
|
if (displaySong != null) { |
|
|
|
await playSong(displaySong); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 下一首 |
|
|
|
/// 下一首(列表内) |
|
|
|
Future<void> nextSong() async { |
|
|
|
await _qqMusicService.skipToNext(); |
|
|
|
if (playlist.isNotEmpty) { |
|
|
|
final nextIndex = (currentIndex.value + 1) % playlist.length; |
|
|
|
await playPlaylistSong(nextIndex); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 上一首 |
|
|
|
/// 上一首(列表内) |
|
|
|
Future<void> previousSong() async { |
|
|
|
await _qqMusicService.skipToPrevious(); |
|
|
|
if (playlist.isNotEmpty) { |
|
|
|
final prevIndex = |
|
|
|
currentIndex.value > 0 ? currentIndex.value - 1 : playlist.length - 1; |
|
|
|
await playPlaylistSong(prevIndex); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 停止播放 |
|
|
|
Future<void> stopMusic() async { |
|
|
|
await _qqMusicService.stopMusic(); |
|
|
|
displayIsPlaying.value = false; |
|
|
|
} |
|
|
|
|
|
|
|
/// 格式化时间 |
|
|
|
String formatDuration(int milliseconds) { |
|
|
|
return _qqMusicService.formatDuration(milliseconds); |
|
|
|
} |
|
|
|
|
|
|
|
/// 获取当前显示的播放状态(用于UI显示) |
|
|
|
bool get isCurrentSongPlaying { |
|
|
|
final displaySong = displayCurrentSong.value; |
|
|
|
final actualSong = actualCurrentSong.value; |
|
|
|
|
|
|
|
// 只有当显示的歌曲与实际播放的歌曲相同时,才显示播放状态 |
|
|
|
if (displaySong != null && |
|
|
|
actualSong != null && |
|
|
|
displaySong.mid == actualSong.mid) { |
|
|
|
return actualIsPlaying.value; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/// 检查歌曲是否在当前播放列表中 |
|
|
|
bool isSongInPlaylist(SongModel song) { |
|
|
|
return playlist.any((s) => s.mid == song.mid); |
|
|
|
} |
|
|
|
|
|
|
|
/// 更新播放列表(支持多个播放列表场景) |
|
|
|
void updatePlaylist(List<SongModel> newPlaylist) { |
|
|
|
playlist.assignAll(newPlaylist); |
|
|
|
_syncDisplayStateWithActualPlayback(); |
|
|
|
Logger.d('QQMusicPlaylist', '播放列表已更新,歌曲数量: ${newPlaylist.length}'); |
|
|
|
} |
|
|
|
} |
|
|
|
|