瀏覽代碼

修复上/下一首播放记录异常

chendeben 1 年之前
父節點
當前提交
3ceb00a918

+ 0 - 2
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -784,8 +784,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       }
 
       // 先获取当前歌曲信息用于日志
-      const currentSong = this.playlistModel.getCurrentSong();
-      const currentIndex = this.playlistModel.getCurrentIndex();
 
       const moved = this.playlistModel.moveToPrevious(playMode);
       if (!moved) {

+ 82 - 11
entry/src/main/ets/view/LocalMusic.ets

@@ -1068,10 +1068,20 @@ export struct LocalMusic {
       if (ArrayUtil.isNotEmpty(this.songList)) {
         this.isFirstStartPlay = true
         this.sonDataSource.pushArrayData(this.songList)
-        if (this.currentSong === undefined && this.songList.length > 0) {
+        
+        // 不要强制设置为第一首歌曲,保持从UnifiedPlayerService恢复的状态
+        // 只有在完全没有当前歌曲且没有有效索引时才设置默认值
+        if (this.currentSong === undefined && this.curIndex < 0 && this.songList.length > 0) {
           this.isFirstStartPlay = false
+          this.curIndex = 0
           this.currentSong = this.songList[0]
+          LogUtils.getInstance().LOGI('LocalMusic: mkDownLoadDir - No current song found, setting to first song as fallback');
+        } else if (this.currentSong === undefined && this.curIndex >= 0 && this.curIndex < this.songList.length) {
+          // 如果有有效索引但没有当前歌曲,从索引恢复
+          this.currentSong = this.songList[this.curIndex]
+          LogUtils.getInstance().LOGI(`LocalMusic: mkDownLoadDir - Restored current song from index ${this.curIndex}: ${this.currentSong.name}`);
         }
+        
         if (this.currentSong) {
           this.videoUrl = this.currentSong.filePath
           this.name = this.currentSong.name
@@ -1079,7 +1089,7 @@ export struct LocalMusic {
           AppStorage.setOrCreate('currentSong',this.currentSong);
         }
 
-        LogUtils.getInstance().LOGI(`LocalMusic: mkDownLoadDir - UI updated with existing playlist: ${this.songList.length} songs`);
+        LogUtils.getInstance().LOGI(`LocalMusic: mkDownLoadDir - UI updated with existing playlist: ${this.songList.length} songs, current index: ${this.curIndex}, current song: ${this.currentSong?.name || 'none'}`);
       } else {
         this.name = '空空如也'
         LogUtils.getInstance().LOGI('LocalMusic: mkDownLoadDir - No playlist available, showing empty state');
@@ -1119,7 +1129,8 @@ export struct LocalMusic {
   aboutToDisappear() {
     console.info('LifeCycleComponent aboutToDisappear');
     this.knockController?.immersiveDisableListening();
-    this.curIndex = 0
+    // 移除 this.curIndex = 0,避免重置当前播放索引
+    // 保持当前播放状态,让UnifiedPlayerService管理播放状态的持久化
     emitter.off(2);
     emitter.off(101);
     emitter.off(888);
@@ -9978,9 +9989,39 @@ export struct LocalMusic {
       this.animationState = AnimationStatus.Running
       LogUtils.getInstance().LOGI("startPlayOrResumePlay start this.CONTROL_PlayStatus:" + this.CONTROL_PlayStatus)
 
-      // 确保播放列表已同步到UnifiedPlayerService
+      // 智能同步播放列表到UnifiedPlayerService
       if (ArrayUtil.isNotEmpty(this.songList)) {
-        this.unifiedPlayerService.setPlaylist(this.songList, this.curIndex);
+        // 检查UnifiedPlayerService是否已经有播放列表
+        const servicePlaylist = this.unifiedPlayerService.getPlaylist();
+        const serviceIndex = this.unifiedPlayerService.getCurrentIndex();
+        
+        if (ArrayUtil.isEmpty(servicePlaylist)) {
+          // 如果服务没有播放列表,使用LocalMusic的播放列表
+          this.unifiedPlayerService.setPlaylist(this.songList, this.curIndex);
+          LogUtils.getInstance().LOGI(`LocalMusic: Set playlist to UnifiedPlayerService - ${this.songList.length} songs, index ${this.curIndex}`);
+        } else {
+          // 如果服务已有播放列表,同步服务的状态到LocalMusic
+          const serviceSong = this.unifiedPlayerService.getCurrentSong();
+          if (serviceSong && serviceIndex >= 0 && serviceIndex < servicePlaylist.length) {
+            this.songList = servicePlaylist;
+            this.curIndex = serviceIndex;
+            this.currentSong = serviceSong;
+            this.sonDataSource.pushArrayData(this.songList);
+            
+            // 更新UI显示
+            this.videoUrl = serviceSong.filePath;
+            this.name = serviceSong.name;
+            this.artist = serviceSong.artist;
+            this.cover = serviceSong.pixelMapPath;
+            
+            // 同步到AppStorage
+            AppStorage.setOrCreate('songList', this.songList);
+            AppStorage.setOrCreate('currIndex', this.curIndex);
+            AppStorage.setOrCreate('currentSong', this.currentSong);
+            
+            LogUtils.getInstance().LOGI(`LocalMusic: Synced from UnifiedPlayerService - ${this.songList.length} songs, index ${this.curIndex}, song: ${serviceSong.name}`);
+          }
+        }
       }
 
       // 使用UnifiedPlayerService开始播放
@@ -11536,11 +11577,35 @@ export struct LocalMusic {
           return;
         }
 
-        // 确保播放列表已同步到UnifiedPlayerService
-        this.unifiedPlayerService.setPlaylist(this.songList, this.curIndex);
-
         // 使用UnifiedPlayerService播放下一首
         await this.unifiedPlayerService.playNext();
+        // 智能同步播放列表到UnifiedPlayerService
+        const servicePlaylist = this.unifiedPlayerService.getPlaylist();
+        const serviceIndex = this.unifiedPlayerService.getCurrentIndex();
+        
+        if (ArrayUtil.isEmpty(servicePlaylist)) {
+          // 如果服务没有播放列表,使用LocalMusic的播放列表
+          this.unifiedPlayerService.setPlaylist(this.songList, this.curIndex);
+          LogUtils.getInstance().LOGI(`LocalMusic playNext: Set playlist to UnifiedPlayerService - ${this.songList.length} songs, index ${this.curIndex}`);
+        } else {
+          // 如果服务已有播放列表,检查是否需要同步
+          const serviceSong = this.unifiedPlayerService.getCurrentSong();
+          if (serviceSong && serviceIndex >= 0 && serviceIndex < servicePlaylist.length) {
+            // 同步服务的状态到LocalMusic,确保状态一致
+            this.songList = servicePlaylist;
+            this.curIndex = serviceIndex;
+            this.currentSong = serviceSong;
+            this.sonDataSource.pushArrayData(this.songList);
+
+            // 同步到AppStorage
+            AppStorage.setOrCreate('songList', this.songList);
+            AppStorage.setOrCreate('currIndex', this.curIndex);
+            AppStorage.setOrCreate('currentSong', this.currentSong);
+            
+            LogUtils.getInstance().LOGI(`LocalMusic playNext: Synced from UnifiedPlayerService - ${this.songList.length} songs, index ${this.curIndex}, song: ${serviceSong.name}`);
+          }
+        }
+
 
         // 更新本地状态以保持UI同步
         const currentIndex = this.unifiedPlayerService.getCurrentIndex();
@@ -11573,6 +11638,8 @@ export struct LocalMusic {
       // 错误处理:回退到原有逻辑
       this.fallbackPlayNext();
     }
+    // 同步播放列表到UnifiedPlayerService
+    this.syncPlaylistToService();
   }
 
   // 回退到原有的playNext逻辑
@@ -11726,12 +11793,14 @@ export struct LocalMusic {
           return;
         }
 
-        // 确保播放列表已同步到UnifiedPlayerService
-        this.unifiedPlayerService.setPlaylist(this.songList, this.curIndex);
-
         // 使用UnifiedPlayerService播放上一首
         await this.unifiedPlayerService.playPrevious();
 
+        // 智能同步播放列表到UnifiedPlayerService
+        const servicePlaylist = this.unifiedPlayerService.getPlaylist();
+        const serviceIndex = this.unifiedPlayerService.getCurrentIndex();
+
+
         // 更新本地状态以保持UI同步
         const currentIndex = this.unifiedPlayerService.getCurrentIndex();
         const currentSong = this.unifiedPlayerService.getCurrentSong();
@@ -11768,6 +11837,8 @@ export struct LocalMusic {
       // 错误处理:回退到原有逻辑
       this.fallbackPlayPrevious();
     }
+    // 同步播放列表到UnifiedPlayerService
+    this.syncPlaylistToService();
   }
 
   // 回退到原有的playPrevious逻辑

+ 0 - 114
播放上一首问题修复.md

@@ -1,114 +0,0 @@
-# 播放上一首问题修复
-
-## 问题描述
-用户点击"播放上一首"按钮时,虽然播放列表显示有上一首歌曲(`hasNext=true, hasPrevious=true, totalCount=101`),但实际播放的却是下一首歌曲。
-
-## 问题分析
-
-### 日志分析
-从日志中可以看到以下异常流程:
-1. 用户点击"播放上一首"
-2. 系统正确识别有上一首歌曲可播放
-3. 但随即触发了 `Playback completed` 事件
-4. 这导致自动播放逻辑启动,播放了下一首歌曲而不是上一首
-
-### 根本原因
-问题出现在歌曲切换的时序上:
-
-1. `playPrevious()` 调用 `stopSilently()` 停止当前播放
-2. 然后调用 `playNewSongDirectly()` 开始播放新歌曲
-3. 但是停止播放器时触发的 `onPlaybackCompleted` 回调仍然会被执行
-4. 这个回调触发了自动播放逻辑 `handleAutoPlayOnCompletion()`
-5. 自动播放逻辑在顺序播放模式下会播放下一首歌曲
-6. 结果就是用户想要的上一首歌曲被自动播放的下一首歌曲覆盖了
-
-### 竞态条件
-这是一个典型的竞态条件问题:
-- 手动歌曲切换操作(playPrevious/playNext)
-- 自动播放逻辑(onPlaybackCompleted 触发的 handleAutoPlayOnCompletion)
-
-两者同时执行,导致预期外的行为。
-
-## 修复方案
-
-### 1. 添加手动切换标志
-在 `UnifiedPlayerService` 中添加一个标志来标识当前是否正在进行手动歌曲切换:
-
-```typescript
-private isManualSongChange: boolean = false; // 新增:是否正在进行手动歌曲切换
-```
-
-### 2. 在手动切换方法中设置标志
-在 `playNext()`、`playPrevious()` 和 `playSongAtIndex()` 方法中:
-
-```typescript
-async playPrevious(): Promise<void> {
-  try {
-    // 设置手动歌曲切换标志,防止自动播放干扰
-    this.isManualSongChange = true;
-    
-    // ... 原有逻辑 ...
-    
-  } catch (error) {
-    await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
-  } finally {
-    // 确保在任何情况下都清除手动切换标志
-    setTimeout(() => {
-      this.isManualSongChange = false;
-    }, 1000); // 延迟1秒清除,确保播放器状态稳定
-  }
-}
-```
-
-### 3. 在自动播放逻辑中检查标志
-在 `handleAutoPlayOnCompletion()` 方法开头添加检查:
-
-```typescript
-private async handleAutoPlayOnCompletion(): Promise<void> {
-  try {
-    // 检查是否正在进行手动歌曲切换,如果是则跳过自动播放
-    if (this.isManualSongChange) {
-      LogUtils.getInstance().LOGI('UnifiedPlayerService: Manual song change in progress, skipping auto-play');
-      return;
-    }
-    
-    // ... 原有的自动播放逻辑 ...
-  }
-}
-```
-
-## 修复效果
-
-### 修复前的流程
-1. 用户点击"上一首" → `playPrevious()`
-2. `stopSilently()` 停止当前播放
-3. `playNewSongDirectly()` 开始播放上一首
-4. **问题**:`onPlaybackCompleted()` 被触发(来自刚停止的歌曲)
-5. `handleAutoPlayOnCompletion()` 执行自动播放逻辑
-6. 结果:播放了下一首歌曲(覆盖了上一首)
-
-### 修复后的流程
-1. 用户点击"上一首" → `playPrevious()`
-2. 设置 `isManualSongChange = true`
-3. `stopSilently()` 停止当前播放
-4. `playNewSongDirectly()` 开始播放上一首
-5. `onPlaybackCompleted()` 被触发(来自刚停止的歌曲)
-6. `handleAutoPlayOnCompletion()` 检查到 `isManualSongChange = true`,跳过自动播放
-7. 1秒后清除 `isManualSongChange` 标志
-8. 结果:正确播放上一首歌曲
-
-## 相关方法
-- `playNext()` - 播放下一首
-- `playPrevious()` - 播放上一首  
-- `playSongAtIndex()` - 播放指定索引歌曲
-- `handleAutoPlayOnCompletion()` - 自动播放逻辑
-
-## 测试验证
-修复后需要验证:
-1. 点击"上一首"按钮能正确播放上一首歌曲
-2. 点击"下一首"按钮能正确播放下一首歌曲
-3. 歌曲自然播放完成后的自动播放功能仍然正常工作
-4. 各种播放模式(顺序、随机、单曲循环等)下的切换都正常
-
-## 相关文件
-- `entry/src/main/ets/common/service/UnifiedPlayerService.ets`

+ 0 - 141
收藏功能修复验证.md

@@ -1,141 +0,0 @@
-# 收藏功能修复验证
-
-## 问题描述
-根据日志分析,发现收藏歌曲列表获取总数为空的问题:
-```
-UnifiedPlayerService: 当前歌曲为空 - favList:[]
-```
-
-虽然实际上是有收藏歌曲的,但系统播控中心显示的收藏状态始终为 `isFavorite: false`。
-
-## 问题根因分析
-
-### 1. 异步数据加载问题
-`UnifiedPlayerService.getFav()` 方法存在异步处理问题:
-- 方法立即返回 `this.favList`(可能为空)
-- 数据库查询是异步的,但方法不等待查询完成
-- 导致 `getCurrentSongFavoriteState()` 总是获取到空的收藏列表
-
-### 2. 竞态条件问题
-在 `LocalMusic.doFav()` 方法中:
-- 调用 `unifiedPlayerService.updateFavoriteList()` 更新收藏列表
-- 然后手动更新 AVSession 的收藏状态
-- 这与 UnifiedPlayerService 自己的 AVSession 更新产生竞态条件
-
-## 修复方案
-
-### 1. 优化收藏列表获取逻辑
-**文件**: `entry/src/main/ets/common/service/UnifiedPlayerService.ets`
-
-```typescript
-getFav(): Array<VideoItem>{
-  if (this.favList.length>0){
-    console.log("Heanup UnifiedPlayerService favList cached:"+json.stringify( this.favList));
-    return this.favList;
-  }
-  // 如果缓存为空,异步更新但返回空数组,避免阻塞
-  this.getTable().queryByisFav(1, async (result: VideoItem[]) => {
-    console.log("Heanup UnifiedPlayerService favList:"+json.stringify( result));
-    this.favList = result;
-    // 更新收藏列表后,重新更新AVSession状态以反映正确的收藏状态
-    if (result.length > 0) {
-      setTimeout(() => {
-        this.updateSessionPlayState();
-      }, 100);
-    }
-  })
-  return this.favList;
-}
-```
-
-### 2. 改进收藏状态检查逻辑
-```typescript
-private getCurrentSongFavoriteState(): boolean {
-  try {
-    let currentSong = this.playlistModel.getCurrentSong();
-    if (!currentSong) {
-      LogUtils.getInstance().LOGI(`UnifiedPlayerService: 当前歌曲为空`);
-      return false;
-    }
-    
-    let favList = this.getFav();
-    if (!favList || favList.length === 0) {
-      // 如果收藏列表为空,可能还在加载中,先返回false
-      // 但不记录为错误,因为这是正常的初始化状态
-      return false;
-    }
-    
-    // 检查当前歌曲是否在收藏列表中
-    for (let i = 0; i < favList.length; i++) {
-      if (favList[i].filePath === currentSong.filePath && favList[i].isFav === 1) {
-        LogUtils.getInstance().LOGI(`UnifiedPlayerService: 收藏歌曲:${currentSong.filePath}`);
-        return true;
-      }
-    }
-    LogUtils.getInstance().LOGI(`UnifiedPlayerService: 未收藏歌曲:${currentSong.filePath}`);
-    return false;
-  } catch (error) {
-    LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to get favorite state: ${error}`);
-    return false;
-  }
-}
-```
-
-### 3. 添加收藏状态刷新方法
-```typescript
-/**
- * 强制刷新收藏状态(用于收藏/取消收藏操作后)
- */
-public async refreshFavoriteStatus(): Promise<void> {
-  try {
-    // 清空缓存,强制重新加载
-    this.favList = [];
-    await this.updateFavoriteList();
-    LogUtils.getInstance().LOGI('UnifiedPlayerService: Favorite status refreshed');
-  } catch (error) {
-    LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to refresh favorite status: ${error}`);
-  }
-}
-```
-
-### 4. 优化收藏操作处理
-**文件**: `entry/src/main/ets/view/LocalMusic.ets`
-
-将原来的手动 AVSession 更新替换为统一的刷新方法:
-```typescript
-// 刷新UnifiedPlayerService的收藏状态,这会自动更新AVSession
-await this.unifiedPlayerService.refreshFavoriteStatus();
-```
-
-### 5. 并行初始化收藏列表
-在服务初始化时并行加载收藏列表:
-```typescript
-// 并行初始化收藏列表和恢复数据,提高效率
-const initPromises2 = [
-  this.restorePersistedState(),
-  this.initializeFavoriteList()
-];
-
-await Promise.all(initPromises2);
-```
-
-## 预期效果
-
-修复后应该能够:
-1. 正确加载收藏歌曲列表
-2. 在系统播控中心正确显示收藏状态
-3. 收藏/取消收藏操作后立即更新播控状态
-4. 避免竞态条件和重复的 AVSession 更新
-
-## 验证方法
-
-1. 启动应用,播放一首已收藏的歌曲
-2. 检查系统播控中心是否显示心形图标(收藏状态)
-3. 在应用中取消收藏,检查播控中心是否立即更新
-4. 重新收藏,检查播控中心是否立即显示收藏状态
-5. 查看日志,确认不再出现 "当前歌曲为空 - favList:[]" 的错误信息
-
-## 相关文件
-- `entry/src/main/ets/common/service/UnifiedPlayerService.ets`
-- `entry/src/main/ets/view/LocalMusic.ets`
-- `entry/src/main/ets/common/util/MediaTable.ets`