فهرست منبع

修复记忆播放

chendeben 1 سال پیش
والد
کامیت
f4dd00da29

+ 14 - 0
entry/src/main/ets/common/service/PlayerManager.ets

@@ -36,6 +36,7 @@ export interface IPlayerManager {
   startPlayOrResumePlay(): Promise<void>;
   pausePlayback(): void;
   stopPlayback(): void;
+  stopPlaybackSilently(): void; // 新增:静默停止,不触发回调
   seekToPosition(position: string): Promise<void>;
   setPlaybackSpeed(speed: string): void;
   setVolume(leftVolume: string, rightVolume: string): void;
@@ -258,6 +259,19 @@ export class PlayerManager implements IPlayerManager {
     } catch (error) {
       LogUtils.getInstance().LOGI(`PlayerManager stopPlayback error: ${error}`);
     }
+  }
+
+  // 新增:静默停止播放,不触发回调(用于歌曲切换)
+  stopPlaybackSilently(): void {
+    try {
+      this.mIjkMediaPlayer.stop();
+      this.isPaused = false; // 清除暂停状态
+      
+      // 不触发 onPlaybackCompleted 回调
+      LogUtils.getInstance().LOGI('PlayerManager: Playback stopped silently without callback');
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`PlayerManager stopPlaybackSilently error: ${error}`);
+    }
   } 
  async seekToPosition(value: string): Promise<void> {
     //private seekTo(value: string) {

+ 134 - 47
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -859,7 +859,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
     // 立即更新基础元数据(使用解析的duration),后续在播放器准备好后会用实际duration更新
     await this.updateAvSessionMetadata(song);
-    this.restorePlaybackPosition(song);
+    
+    // 注释掉此处的记忆播放恢复,统一在onPrepared中处理
+    // this.restorePlaybackPosition(song);
+    
     this.currentRetryCount = 0;
   }
 
@@ -918,7 +921,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
 
       // 保存当前播放位置
-      this.savePlaybackPosition();
+      const currentSong = this.playlistModel.getCurrentSong();
+      if (currentSong) {
+        this.savePlaybackPosition(currentSong);
+      }
 
       // 暂停播放器 - 确保先暂停播放器
       this.playerManager.pausePlayback();
@@ -951,7 +957,12 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
   async stop(): Promise<void> {
     try {
-      this.savePlaybackPosition();
+      // 保存当前歌曲的播放位置
+      const currentSong = this.playlistModel.getCurrentSong();
+      if (currentSong) {
+        this.savePlaybackPosition(currentSong);
+      }
+      
       this.playerManager.stopPlayback();
 
       // 重要:停止时应该清除所有播放状态,不是暂停状态
@@ -978,13 +989,26 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    */
   private async stopSilently(): Promise<void> {
     try {
-      this.savePlaybackPosition();
+      // 静默停止播放,不保存播放位置(位置已经在外部保存)
       this.playerManager.stopPlayback();
       this.stopProgressTimer();
       // 不调用 updatePlayingState,避免触发状态变化通知
 
     } catch (error) {
-      LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService stopSilently error: ${error}`);
+      LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService stopSilentlyWithoutSave error: ${error}`);
+      throw new Error;
+    }
+  }
+
+  private async stopSilentlyWithoutSave(): Promise<void> {
+    try {
+      // 静默停止播放,不保存播放位置(位置已经在外部保存),也不触发回调
+      this.playerManager.stopPlaybackSilently();
+      this.stopProgressTimer();
+      // 不调用 updatePlayingState,避免触发状态变化通知
+
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService stopSilentlyWithoutSave error: ${error}`);
       throw new Error;
     }
   }
@@ -1038,6 +1062,13 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         }
       }
 
+      // **关键修复:在移动播放列表位置之前先获取并保存当前歌曲的播放位置**
+      const oldSong = this.playlistModel.getCurrentSong();
+      if (oldSong) {
+        this.savePlaybackPosition(oldSong);
+        LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 Saved old song position before track change: ${oldSong.name}`);
+      }
+
       const playMode = this.stateModel.getState().playMode;
       let moved = false;
 
@@ -1067,8 +1098,8 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         this.stateModel.updateCurrentSong(newSong);
       }
 
-      // 静默停止当前播放
-      await this.stopSilently();
+      // 静默停止当前播放(不再保存位置,因为已经在上面保存了)
+      await this.stopSilentlyWithoutSave();
 
       // 直接播放新歌曲
       await this.playNewSongDirectly(newSong);
@@ -2079,6 +2110,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
     // 重置播放器 - 这会清除所有状态包括播放位置
     ijkPlayer.reset();
+    
+    // 清空当前加载的歌曲记录
+    this.currentlyLoadedSong = null;
 
     // 重新设置音频模式 - 重置后需要重新设置
     ijkPlayer.setAudioId('unifiedPlayer');
@@ -2103,6 +2137,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
     // 设置数据源
     ijkPlayer.setDataSource(song.filePath);
+    
+    // 记录当前播放器加载的歌曲
+    this.currentlyLoadedSong = song;
+    
     LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Set data source to ${song.filePath}`);
 
     // 优化:异步设置HTTP请求头和播放速度,不阻塞主流程
@@ -2210,21 +2248,47 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     }
   }
 
-  private savePlaybackPosition(): void {
-    const ijkPlayer = this.playerManager.getIjkPlayer();
-    if (ijkPlayer != null) {
-      const position = ijkPlayer.getCurrentPosition();
-      // ToastUtil.showToast('保存记忆播放 = ' + position)
-      const duration = ijkPlayer.getDuration();
-      const threshold = 5000; // 阈值,单位为毫秒(这里设为5秒)
+  // 记录当前播放器实际加载的歌曲
+  private currentlyLoadedSong: VideoItem | null = null;
 
-      // 如果播放位置接近视频末尾,则保存 position 为 0
-      const playbackPosition = (duration - position < threshold) ? 0 : position;
-      const currentSong = this.playlistModel.getCurrentSong();
-      if (currentSong != null) {
-        PuraPreferencesUtil.putSync(currentSong.filePath, playbackPosition);
+  private savePlaybackPosition(targetSong?: VideoItem): void {
+    try {
+      // 检查用户是否启用了记忆播放功能
+      const isMusicMemoryPlay = PuraPreferencesUtil.getBooleanSync(SettingPage.iS_MUSIC_MEMORY_PLAY, false);
+      if (!isMusicMemoryPlay) {
+        return; // 用户未启用,直接返回
+      }
+
+      const ijkPlayer = this.playerManager.getIjkPlayer();
+      let currentSong = targetSong;
+      
+      // 如果没有传入具体歌曲,优先使用播放器当前加载的歌曲
+      if (!currentSong) {
+        currentSong = this.currentlyLoadedSong || this.playlistModel.getCurrentSong()!;
       }
+      
+      if (ijkPlayer != null && currentSong != null) {
+        const position = ijkPlayer.getCurrentPosition();
+        const duration = ijkPlayer.getDuration();
+        const threshold = 5000; // 阈值,单位为毫秒(这里设为5秒)
 
+        // 如果播放位置接近视频末尾,则保存 position 为 0
+        const playbackPosition = (duration - position < threshold) ? 0 : position;
+        
+        // 使用filePath作为键,确保与恢复时一致
+        PuraPreferencesUtil.putSync(currentSong.filePath, playbackPosition);
+        
+        // 同时保存到AppStorage,确保卡片也能访问(使用相同的键)
+        AppStorage.setOrCreate(`playback_${currentSong.filePath}`, playbackPosition);
+        
+        // 获取调用栈信息用于调试
+        const stack = new Error().stack;
+        const caller = stack?.split('\n')[2]?.trim() || 'Unknown caller';
+        
+        LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 Saved playback position: ${playbackPosition}ms for ${currentSong.name} (filePath: ${currentSong.filePath}) - Called from: ${caller}`);
+      }
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 savePlaybackPosition error: ${error}`);
     }
   }
 
@@ -2414,7 +2478,21 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         this.dataPersistence.savePlayerState(stoppedState);
       }
 
-      this.savePlaybackPosition();
+      // 修复:只在播放位置有意义时才保存,避免在歌曲刚开始时覆盖记忆播放位置
+      const ijkPlayer = this.playerManager.getIjkPlayer();
+      if (ijkPlayer && this.isPlayerPreparedForCurrentSong) {
+        const currentPosition = ijkPlayer.getCurrentPosition();
+        // 只有当播放位置大于2秒时才保存,避免覆盖记忆播放位置
+        if (currentPosition > 2000) {
+          const currentSong = this.playlistModel.getCurrentSong();
+          if (currentSong) {
+            this.savePlaybackPosition(currentSong);
+          }
+        } else {
+          LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Skipping playback position save - position too small (${currentPosition}ms)`);
+        }
+      }
+      
       this.syncPlaylistToStorage();
 
       LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Current state saved successfully`);
@@ -2451,14 +2529,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    * 播放列表同步完成回调
    */
   onPlaylistSynced(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void {
-    try {
-      LogUtils.getInstance()
-        .LOGI(`Heanup UnifiedPlayerService: Playlist synced - ${playlist.length} songs, index ${currentIndex}`);
 
-      // 播放列表同步完成,无需额外通知
-    } catch (error) {
-      LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onPlaylistSynced error: ${error}`);
-    }
   }
 
   /**
@@ -2651,7 +2722,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
       // 更新桌面卡片
       await this.updateAllForms(true); // 强制更新
-      LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Current state broadcasted manually');
     } catch (error) {
       LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService broadcastCurrentState error: ${error}`);
       throw new Error;
@@ -2688,7 +2758,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       LogUtils.getInstance().LOGI(`onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`);
 
       // 首先保存当前播放位置
-      await this.savePlaybackPosition();
+      const currentSong = this.playlistModel.getCurrentSong();
+      if (currentSong) {
+        this.savePlaybackPosition(currentSong);
+      }
 
       switch (event.hintType) {
         case InterruptHintType.INTERRUPT_HINT_PAUSE:
@@ -2794,19 +2867,14 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   // ==================== PlayerStateCallback 实现 ====================
 
   onPrepared(): void {
-    LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Player prepared');
-    this.stateModel.updateLoadingState(false);
-
     // 标记播放器已为当前歌曲准备就绪
     this.isPlayerPreparedForCurrentSong = true;
     console.log("Heanup2 UnifiedPlayerService: onPrepared - 播放器已为当前歌曲准备就绪");
 
-    // 执行跳过片头和记忆播放恢复逻辑
     setTimeout(() => {
+      // 执行跳过片头和记忆播放恢复逻辑
       this.checkAndHandleOnPreparedLogic();
-    }, 200);
-
-    setTimeout(() => {
+      this.stateModel.updateLoadingState(false);
       const ijkPlayer = this.playerManager.getIjkPlayer();
       const isActuallyPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
 
@@ -2846,6 +2914,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    * 包括跳过片头和记忆播放恢复
    */
   private checkAndHandleOnPreparedLogic(): void {
+    LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: 跳过片头和记忆播放恢复 Checking onPrepared logic');
     try {
       const currentSong = this.playlistModel.getCurrentSong();
       if (!currentSong) {
@@ -2860,6 +2929,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       const jumpSettings = this.loadJumpSettings(currentSong);
       
       if (jumpSettings.isEnabled && jumpSettings.jumpTopTime > 0) {
+        console.log(`Heanup UnifiedPlayerService: Jump settings enabled: ${jumpSettings.jumpTopTime}s`)
         if (jumpSettings.jumpTopTime * 1000 <= duration) {
           // 需要跳过片头
           if (videoUrl && !videoUrl.toLowerCase().endsWith('.ts')) {
@@ -2907,31 +2977,36 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    */
   private handleMemoryPlayback(currentSong: VideoItem, videoUrl: string): void {
     try {
+      console.log(`Heanup 记忆播放功能: 处理记忆播放 for ${currentSong.name}`)
+      
       // 检查用户是否启用了记忆播放功能
       const isMusicMemoryPlay: boolean = PuraPreferencesUtil.getBooleanSync(SettingPage.iS_MUSIC_MEMORY_PLAY, false);
       const isMemoryLastPlay: boolean = PuraPreferencesUtil.getBooleanSync(SettingPage.iS_MEMORY_LAST_PLAY, false);
       
+      LogUtils.getInstance().LOGI(`Heanup 记忆播放功能: Settings - isMusicMemoryPlay: ${isMusicMemoryPlay}, isMemoryLastPlay: ${isMemoryLastPlay}`);
+      
       // 只在首次启动时检查 isMemoryLastPlay
-      // 这里我们假设如果是首次播放,会有特殊标记
       if (!isMusicMemoryPlay && !isMemoryLastPlay) {
-        LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Memory play disabled, skipping restore');
+        LogUtils.getInstance().LOGI('Heanup 记忆播放功能 : Memory play disabled, skipping restore');
         return;
       }
 
-      if (videoUrl && !videoUrl.toLowerCase().endsWith('.ts')) {
-        // 优先从 AppStorage 获取,如果没有则从 PreferencesUtil 获取
-        let position: number = AppStorage.get<number>(`playback_${videoUrl}`) || 0;
-        if (position === 0) {
-          position = PuraPreferencesUtil.getNumberSync(videoUrl, 0);
-        }
-
+      if (currentSong && currentSong.filePath && !currentSong.filePath.toLowerCase().endsWith('.ts')) {
+        // 使用 currentSong.filePath 作为键,确保与保存时一致
+        let position = PuraPreferencesUtil.getNumberSync(currentSong.filePath, 0);
+        LogUtils.getInstance().LOGI(`Heanup 记忆播放功能: Retrieved position ${position}ms from key: ${currentSong.filePath}`);
+        
         if (position > 0) {
-          LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restoring playback position: ${position}ms for ${currentSong.name}`);
+          LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 : Restoring playback position: ${position}ms for ${currentSong.name}`);
           this.seekTo(position.toString());
+        } else {
+          LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 : No saved position found for ${currentSong.name} (key: ${currentSong.filePath})`);
         }
+      } else {
+        LogUtils.getInstance().LOGI(`Heanup 记忆播放功能: Skipping restore - invalid song or .ts file: ${currentSong?.filePath}`);
       }
     } catch (error) {
-      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error in handleMemoryPlayback: ${error}`);
+      LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 : Error in handleMemoryPlayback: ${error}`);
     }
   }
 
@@ -2988,6 +3063,18 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
   onPlaybackCompleted(): void {
     LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Playback completed');
+    
+    // **关键修复:在手动切换歌曲时,不要保存播放位置,因为已经在 changeTrack 中保存了**
+    if (!this.isManualSongChange) {
+      // 只有在非手动切换时才保存当前歌曲的播放位置(如自然播放完成)
+      const completedSong = this.playlistModel.getCurrentSong();
+      if (completedSong) {
+        this.savePlaybackPosition(completedSong);
+      }
+    } else {
+      LogUtils.getInstance().LOGI('Heanup 记忆播放功能: Skipping save in onPlaybackCompleted due to manual song change');
+    }
+    
     this.stateModel.updatePlayingState(false);
     this.stopProgressTimer();
 

+ 6 - 26
entry/src/main/ets/view/LocalMusic.ets

@@ -12135,33 +12135,11 @@ export struct LocalMusic {
     }
   }
 
-  //保持记忆播放功能
+  //保持记忆播放功能 - 已迁移到UnifiedPlayerService,此方法已废弃
   private savePlaybackPosition() {
-    try {
-      // 检查用户是否启用了记忆播放功能
-      const isMusicMemoryPlay = PreferencesUtil.getBooleanSync(SettingPage.iS_MUSIC_MEMORY_PLAY, false);
-      if (!isMusicMemoryPlay) {
-        LogUtils.getInstance().LOGI('记忆播放功能已关闭,跳过保存播放位置');
-        return; // 用户未启用,直接返回
-      }
-
-      // 通过UnifiedPlayerService获取当前播放位置
-      const currentState = this.unifiedPlayerService.getCurrentState();
-      const position = currentState.currentPosition;
-      const duration = currentState.duration;
-      const threshold = 5000; // 阈值,单位为毫秒(这里设为5秒)
-
-      // 如果播放位置接近视频末尾,则保存 position 为 0
-      const playbackPosition = (duration - position < threshold) ? 0 : position;
-
-      // 同时保存到PreferencesUtil和AppStorage,确保卡片也能访问
-      PreferencesUtil.putSync(this.videoUrl, playbackPosition);
-      AppStorage.setOrCreate(`playback_${this.videoUrl}`, playbackPosition);
-
-      LogUtils.getInstance().LOGI(`Saved playback position: ${playbackPosition}ms for ${this.name}`);
-    } catch (error) {
-      LogUtils.getInstance().LOGI(`savePlaybackPosition error: ${error}`);
-    }
+    // 记忆播放功能已完全迁移到UnifiedPlayerService中统一处理
+    // 这里保留空实现以避免调用处报错,但实际功能由UnifiedPlayerService负责
+    LogUtils.getInstance().LOGI('LocalMusic savePlaybackPosition: 记忆播放功能已迁移到UnifiedPlayerService');
   }
 
 
@@ -12258,6 +12236,7 @@ export struct LocalMusic {
         }
 
         // 使用UnifiedPlayerService播放下一首,支持所有播放模式(包括随机播放)
+        this.savePlaybackPosition();
         await this.unifiedPlayerService.playNext();
 
         // 智能同步播放列表到UnifiedPlayerService
@@ -12457,6 +12436,7 @@ export struct LocalMusic {
         }
 
         // 使用UnifiedPlayerService播放上一首,支持所有播放模式(包括随机播放)
+        this.savePlaybackPosition();
         await this.unifiedPlayerService.playPrevious();
 
         // 智能同步播放列表到UnifiedPlayerService