chendeben 1 год назад
Родитель
Сommit
ea78da9adb
1 измененных файлов с 146 добавлено и 33 удалено
  1. 146 33
      entry/src/main/ets/common/service/UnifiedPlayerService.ets

+ 146 - 33
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -12,6 +12,9 @@ import { ErrorRecoveryStrategy, IErrorRecoveryStrategy, ErrorContext, ErrorRecov
 import { AvSessionController } from '../../controller/AvSessionController';
 import { avSession } from '@kit.AVSessionKit';
 import { formProvider, formBindingData } from '@kit.FormKit';
+import { fileIo } from '@kit.CoreFileKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { http } from '@kit.NetworkKit';
 import json from '@ohos.util.json';
 import MediaTable from '../util/MediaTable';
 
@@ -94,6 +97,8 @@ export interface WidgetFormData {
   playerState: WidgetPlayerState;
   playlistInfo: WidgetPlaylistInfo;
   timeInfo: WidgetTimeInfo;
+  // 卡片图片显示必填字段
+  formImages: Record<string, number>;
 }
 
 /**
@@ -336,6 +341,9 @@ interface widgeData {
   playerState: WidgetPlayerState;
   playlistInfo: WidgetPlaylistInfo;
   timeInfo: WidgetTimeInfo;
+
+  // 卡片图片显示必填字段
+  formImages: Record<string, number>;
 }
 
 export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, PlayerStateCallback {
@@ -348,7 +356,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   private errorRecovery: IErrorRecoveryStrategy;
   private avSessionController: AvSessionController | null = null;
   private context: common.UIAbilityContext | null = null;
-  
+
   // 防重复调用机制
   private isUpdatingForms: boolean = false;
   private lastUpdateTime: number = 0;
@@ -586,7 +594,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       this.saveCurrentState();
       this.updateSessionPlayState();
       await this.updateWidgetsForPlayStateChange();
-      
+
       // 同步更新所有桌面卡片状态
       await this.updateAllForms();
     } catch (error) {
@@ -856,7 +864,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         try {
           // 确保播放列表状态是最新的
           this.updatePlaylistStateInModelSync();
-          
+
           // 强制更新所有桌面卡片状态
           await this.updateAllForms(true);
           if (newSong) {
@@ -1971,7 +1979,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
       // 设置一个临时的数据恢复完成标识,允许快速响应
       this.isDataRestored = true;
-      
+
       // 立即更新播放列表状态,确保上一首/下一首状态正确
       this.updatePlaylistStateInModelSync();
     } catch (error) {
@@ -2006,7 +2014,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         }
 
         LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restored playlist with ${playlistData.songs.length} songs`);
-        
+
         // 立即更新播放列表状态,确保上一首/下一首状态正确
         this.updatePlaylistStateInModelSync();
       } else {
@@ -2040,10 +2048,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
       // 设置数据恢复完成标识
       this.isDataRestored = true;
-      
+
       // 立即更新播放列表状态,确保上一首/下一首状态正确
       this.updatePlaylistStateInModelSync();
-      
+
       LogUtils.getInstance().LOGI('UnifiedPlayerService: Data restoration completed');
     } catch (error) {
       console.log(`Heanup2 UnifiedPlayerService: 恢复持久化状态时出错: ${error}`);
@@ -2279,10 +2287,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     try {
       // 确保播放列表状态是最新的
       this.updatePlaylistStateInModelSync();
-      
+
       // 触发状态监听器通知
       this.stateModel.forceNotifyStateChanged();
-      
+
       // 更新桌面卡片
       await this.updateAllForms(true); // 强制更新
       LogUtils.getInstance().LOGI('UnifiedPlayerService: Current state broadcasted manually');
@@ -2430,10 +2438,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
       // 强制同步状态模型
       this.stateModel.updatePlayingState(isActuallyPlaying);
-      
+
       // 确保播放列表状态是最新的
       this.updatePlaylistStateInModelSync();
-      
+
       // 更新 AVSession 状态
       if (isActuallyPlaying) {
         let currentSong= this.getCurrentSong();
@@ -2443,7 +2451,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         }
         this.updateSessionPlayState(true); // 强制更新,不使用防抖
       }
-      
+
       // 延迟更新卡片,确保状态已同步
       setTimeout(() => {
         this.updateWidgetsForStateChange();
@@ -2655,7 +2663,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    */
   private async updateWidgetsForPlayStateChange(): Promise<void> {
     try {
-       this.updateAllForms();
+      this.updateAllForms();
       LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widgets updated for play state change`);
     } catch (error) {
       LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update widgets for play state change: ${error}`);
@@ -2667,24 +2675,27 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    * 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
    */
   async updateAllForms(forceUpdate: boolean = false) {
+    // 定义变量在方法开始处,确保在整个方法中都可以访问
+    let imgMap: Record<string, number> = {};
+
     try {
       const now = Date.now();
-      
+
       // 防重复调用:如果正在更新或距离上次更新时间太近,则跳过(除非强制更新)
       if (!forceUpdate && (this.isUpdatingForms || (now - this.lastUpdateTime) < this.UPDATE_THROTTLE_MS)) {
         LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Skipping duplicate call (last update: ${now - this.lastUpdateTime}ms ago)`);
         return;
       }
-      
+
       this.isUpdatingForms = true;
       this.lastUpdateTime = now;
-      
+
       // 确保播放列表状态是最新的
       this.updatePlaylistStateInModelSync();
-      
+
       const formData: VideoItem | null = this.getCurrentSong(); // VideoItem
       const songState: PlayerState = this.getCurrentState(); // PlayerState
-      
+
       if (!formData) {
         LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No current song data');
         return;
@@ -2717,18 +2728,84 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         hasPrevious: songState.hasPrevious || false,
         playMode: songState.playMode || 0
       };
-      
+
       const widgetPlaylistInfo: WidgetPlaylistInfo = {
         currentIndex: this.getCurrentIndex(),
         totalCount: this.getPlaylist().length
       };
-      
+
       const widgetTimeInfo: WidgetTimeInfo = {
         currentTimeText: this.formatTimeDisplay(Math.floor((songState.currentPosition || 0) / 1000)),
         totalTimeText: this.formatTimeDisplay(Math.floor((songState.duration || 0) / 1000)),
         progressPercentage: this.calculateProgressPercentage(songState.currentPosition || 0, songState.duration || 0)
       };
 
+      // 处理封面图片
+      let imgName: string = '';
+
+      // 如果歌曲有封面图片路径,尝试处理图片
+      if (formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
+        try {
+          // 生成唯一的图片名称,确保每次更新时图片能正确刷新
+          imgName = 'songCover_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+
+          // 判断是否为网络图片
+          if (formData.pixelMapPath.startsWith('http://') || formData.pixelMapPath.startsWith('https://')) {
+            // 处理网络图片:下载到临时文件
+            LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Downloading network image ${formData.pixelMapPath}`);
+
+            const tempDir = this.context?.getApplicationContext().tempDir;
+            if (!tempDir) {
+              LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: No temp directory available for network image`);
+            } else {
+              const tempFileName = imgName + '.tmp';
+              const tempFilePath = tempDir + '/' + tempFileName;
+
+              // 创建HTTP请求下载图片
+              const httpRequest = http.createHttp();
+              try {
+                const response = await httpRequest.request(formData.pixelMapPath, {
+                  method: http.RequestMethod.GET,
+                  connectTimeout: 5000, // 5秒超时,避免卡片更新被阻塞
+                  readTimeout: 5000
+                });
+
+                if (response.responseCode === http.ResponseCode.OK && response.result) {
+                  // 将下载的图片数据写入临时文件
+                  const tempFile = fileIo.openSync(tempFilePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
+
+                  try {
+                    await fileIo.write(tempFile.fd, response.result as ArrayBuffer);
+                    imgMap[imgName] = tempFile.fd;
+
+                    LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Downloaded and opened network image ${formData.pixelMapPath} to ${tempFilePath} with fd ${tempFile.fd}, imgName: ${imgName}`);
+                  } catch (writeError) {
+                    fileIo.closeSync(tempFile);
+                    // throw  writeError;
+                  }
+                } else {
+                  LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to download network image ${formData.pixelMapPath}: HTTP ${response.responseCode}`);
+                }
+              } finally {
+                httpRequest.destroy();
+              }
+            }
+          } else {
+            // 处理本地图片文件
+            let file = fileIo.openSync(formData.pixelMapPath, fileIo.OpenMode.READ_ONLY);
+            imgMap[imgName] = file.fd;
+
+            LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Opened local cover image ${formData.pixelMapPath} with fd ${file.fd}, imgName: ${imgName}`);
+          }
+        } catch (error) {
+          LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to process cover image ${formData.pixelMapPath}: ${JSON.stringify(error as BusinessError)}`);
+          // 如果处理图片失败,使用空的imgMap
+          imgMap = {};
+          imgName = '';
+        }
+      } else {
+        LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: No cover image path available`);
+      }
 
       // 平铺数据结构,确保桌面卡片能正确接收
       const flatWidgetData: widgeData = {
@@ -2740,9 +2817,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         pixelMapPath: formData.pixelMapPath || '',
         duration: typeof formData.duration === 'string' ? parseInt(formData.duration || '0') : (formData.duration || 0),
         filePath: formData.filePath || '',
-        imgName: '',
+        imgName: imgName, // 使用生成的唯一图片名称
         imageColorHex: '2A2A2A',
-        
+
         // PlayerState数据平铺
         isPlaying: widgetPlayerState.isPlaying,
         isPaused: widgetPlayerState.isPaused,
@@ -2751,20 +2828,23 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         hasNext: widgetPlayerState.hasNext,
         hasPrevious: widgetPlayerState.hasPrevious,
         playMode: widgetPlayerState.playMode,
-        
+
         // 播放列表信息平铺
         currentIndex: widgetPlaylistInfo.currentIndex,
         totalCount: widgetPlaylistInfo.totalCount,
-        
+
         // 时间信息平铺
         currentTimeText: widgetTimeInfo.currentTimeText,
         totalTimeText: widgetTimeInfo.totalTimeText,
         progressPercentage: widgetTimeInfo.progressPercentage,
-        
+
         // 保持嵌套结构兼容性
         playerState: widgetPlayerState,
         playlistInfo: widgetPlaylistInfo,
-        timeInfo: widgetTimeInfo
+        timeInfo: widgetTimeInfo,
+
+        // 卡片图片显示必填字段
+        formImages: imgMap
       };
 
       // 记录更新统计
@@ -2782,10 +2862,10 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         } catch (error) {
           failedCount++;
           const errorStr: string = error?.toString() || '';
-          
+
           // 检查是否是Form ID不存在的错误,并且增加更严格的判断条件
-          if ((errorStr.includes('does not exist') || errorStr.includes('不存在')) && 
-              !errorStr.includes('timeout') && !errorStr.includes('busy')) {
+          if ((errorStr.includes('does not exist') || errorStr.includes('不存在')) &&
+            !errorStr.includes('timeout') && !errorStr.includes('busy')) {
             // 只有明确是"不存在"错误且不是临时性错误时才标记为无效
             invalidFormIds.push(formId);
             LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Form ${formId} does not exist, will be removed from storage`);
@@ -2806,9 +2886,42 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       }
 
       LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Updated ${successCount}/${formIds.length} forms with song "${formData.name}", playing: ${songState.isPlaying}, failed: ${failedCount}, cleaned: ${invalidFormIds.length}`);
+
     } catch (error) {
       LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms error: ${error}`);
     } finally {
+      // 关闭图片文件描述符并清理临时文件
+      try {
+        const tempDir = this.context?.getApplicationContext().tempDir;
+
+        // 遍历imgMap中的所有条目
+        const imgKeys = Object.keys(imgMap);
+        for (let i = 0; i < imgKeys.length; i++) {
+          const imgKey = imgKeys[i];
+          const fd = imgMap[imgKey];
+          if (typeof fd === 'number') {
+            // 关闭文件描述符
+            fileIo.closeSync( fd);
+            LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Closed image file descriptor ${fd} for ${imgKey}`);
+
+            // 如果是网络图片的临时文件,尝试删除
+            if (tempDir && imgKey.startsWith('songCover_')) {
+              try {
+                const tempFilePath = tempDir + '/' + imgKey + '.tmp';
+                if (fileIo.accessSync(tempFilePath)) {
+                  fileIo.unlinkSync(tempFilePath);
+                  LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Deleted temp file ${tempFilePath}`);
+                }
+              } catch (deleteError) {
+                LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to delete temp file for ${imgKey}: ${deleteError}`);
+              }
+            }
+          }
+        }
+      } catch (error) {
+        LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to close image file descriptors: ${error}`);
+      }
+
       // 重置更新标志
       this.isUpdatingForms = false;
     }
@@ -2836,7 +2949,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    */
   private async updateWidgetsForSongChange(song: VideoItem, forceUpdate: boolean = false): Promise<void> {
 
-      this.updateAllForms();
+    this.updateAllForms();
 
   }
 
@@ -3049,17 +3162,17 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     const playerDuration = ijkPlayer ? ijkPlayer.getDuration() : 0;
     const shouldWaitForPlayer = playerDuration <= 0 && this.isPlayerPreparedForCurrentSong === false;
     this.avMetadataUpdateTimer = setTimeout( () => {
-       this.doUpdateAvSessionMetadata(song);
+      this.doUpdateAvSessionMetadata(song);
     }, 800);
     if (shouldWaitForPlayer) {
       // 播放器还没准备好,延迟更新
       this.avMetadataUpdateTimer = setTimeout( () => {
-         this.doUpdateAvSessionMetadata(song);
+        this.doUpdateAvSessionMetadata(song);
       }, 800);
     } else {
       // 播放器已准备好或者不需要等待,稍微延迟更新
       this.avMetadataUpdateTimer = setTimeout( () => {
-         this.doUpdateAvSessionMetadata(song);
+        this.doUpdateAvSessionMetadata(song);
       }, 800);
     }
   }