|
|
@@ -2,15 +2,16 @@ import { VideoItem } from '../../viewmodel/VideoItem';
|
|
|
import { PlayerManager, IPlayerManager, PlayerStateCallback } from './PlayerManager';
|
|
|
import { PlayerStateModel, PlayerState, PlayerStateListener, PlayMode, PlayerError, PlayerErrorType } from './PlayerStateModel';
|
|
|
import { PlaylistModel } from './PlaylistModel';
|
|
|
-import { PlayProgress, WidgetData, WidgetSize, WidgetTheme } from '../widget/WidgetTypes';
|
|
|
import { IjkMediaPlayer, LogUtils, InterruptEvent, InterruptHintType } from '@ohos/ijkplayer';
|
|
|
import { common } from '@kit.AbilityKit';
|
|
|
-import { PreferencesUtil, StrUtil } from '@pura/harmony-utils';
|
|
|
+import { PreferencesUtil as PuraPreferencesUtil, StrUtil } from '@pura/harmony-utils';
|
|
|
+import { PreferencesUtil } from '../utils/PreferencesUtil';
|
|
|
import { DataPersistenceService, PlaylistSyncService, PlaylistSyncListener, IDataPersistenceService, PlaylistData,
|
|
|
PlayerStateData } from './DataPersistenceService';
|
|
|
import { ErrorRecoveryStrategy, IErrorRecoveryStrategy, ErrorContext, ErrorRecoveryAction } from './ErrorRecoveryStrategy';
|
|
|
import { AvSessionController } from '../../controller/AvSessionController';
|
|
|
import { avSession } from '@kit.AVSessionKit';
|
|
|
+import { formProvider, formBindingData } from '@kit.FormKit';
|
|
|
import json from '@ohos.util.json';
|
|
|
import MediaTable from '../util/MediaTable';
|
|
|
|
|
|
@@ -43,6 +44,58 @@ export interface InitializationValidation {
|
|
|
warnings: string[];
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 卡片播放状态数据接口
|
|
|
+ */
|
|
|
+export interface WidgetPlayerState {
|
|
|
+ isPlaying: boolean;
|
|
|
+ isPaused: boolean;
|
|
|
+ isLoading: boolean;
|
|
|
+ currentPosition: number;
|
|
|
+ duration: number;
|
|
|
+ hasNext: boolean;
|
|
|
+ hasPrevious: boolean;
|
|
|
+ playMode: number;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 卡片播放列表信息接口
|
|
|
+ */
|
|
|
+export interface WidgetPlaylistInfo {
|
|
|
+ currentIndex: number;
|
|
|
+ totalCount: number;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 卡片时间信息接口
|
|
|
+ */
|
|
|
+export interface WidgetTimeInfo {
|
|
|
+ currentTimeText: string;
|
|
|
+ totalTimeText: string;
|
|
|
+ progressPercentage: number;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 卡片数据接口
|
|
|
+ */
|
|
|
+export interface WidgetFormData {
|
|
|
+ // VideoItem数据
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ artist: string;
|
|
|
+ album: string;
|
|
|
+ pixelMapPath: string;
|
|
|
+ duration: number;
|
|
|
+ filePath: string;
|
|
|
+ // 扩展数据
|
|
|
+ imgName: string;
|
|
|
+ imageColorHex: string;
|
|
|
+ // 状态数据
|
|
|
+ playerState: WidgetPlayerState;
|
|
|
+ playlistInfo: WidgetPlaylistInfo;
|
|
|
+ timeInfo: WidgetTimeInfo;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 统一播放器服务接口
|
|
|
* 提供统一的播放控制接口,替代LocalMusic中的播放器逻辑
|
|
|
@@ -249,6 +302,42 @@ export interface IPlayerService {
|
|
|
* 统一播放器服务实现
|
|
|
* 整合PlayerManager、PlayerStateModel和PlaylistModel
|
|
|
*/
|
|
|
+interface widgeData {
|
|
|
+ // VideoItem数据平铺
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ artist: string;
|
|
|
+ album: string;
|
|
|
+ pixelMapPath: string;
|
|
|
+ duration: number;
|
|
|
+ filePath: string;
|
|
|
+ imgName: string;
|
|
|
+ imageColorHex: string;
|
|
|
+
|
|
|
+ // PlayerState数据平铺
|
|
|
+ isPlaying: boolean;
|
|
|
+ isPaused: boolean;
|
|
|
+ isLoading: boolean;
|
|
|
+ currentPosition: number;
|
|
|
+ hasNext: boolean;
|
|
|
+ hasPrevious: boolean;
|
|
|
+ playMode: number;
|
|
|
+
|
|
|
+ // 播放列表信息平铺
|
|
|
+ currentIndex: number;
|
|
|
+ totalCount: number;
|
|
|
+
|
|
|
+ // 时间信息平铺
|
|
|
+ currentTimeText: string;
|
|
|
+ totalTimeText: string;
|
|
|
+ progressPercentage: number;
|
|
|
+
|
|
|
+ // 保持嵌套结构兼容性
|
|
|
+ playerState: WidgetPlayerState;
|
|
|
+ playlistInfo: WidgetPlaylistInfo;
|
|
|
+ timeInfo: WidgetTimeInfo;
|
|
|
+}
|
|
|
+
|
|
|
export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, PlayerStateCallback {
|
|
|
private static instance: UnifiedPlayerService | null = null;
|
|
|
private playerManager: IPlayerManager;
|
|
|
@@ -259,6 +348,11 @@ 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;
|
|
|
+ private readonly UPDATE_THROTTLE_MS: number = 100; // 100ms内只允许一次更新
|
|
|
private progressTimer: number = -1;
|
|
|
private isInitialized: boolean = false;
|
|
|
private isDataRestored: boolean = false; // 新增:数据恢复完成标识
|
|
|
@@ -269,7 +363,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
private lastAvMetadataUpdate: number = 0; // 新增:上次元数据更新时间
|
|
|
private avSessionUpdateTimer: number = -1; // 新增:系统播控更新防抖定时器
|
|
|
private avMetadataUpdateTimer: number = -1; // 新增:元数据更新防抖定时器
|
|
|
- private lastUpdateTime: number=0;
|
|
|
private favList: VideoItem[]=[];
|
|
|
private table: MediaTable | undefined = undefined;
|
|
|
private lastAutoPlayTime: number = 0; // 新增:上次自动播放时间,用于防抖
|
|
|
@@ -499,6 +592,8 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
this.startProgressTimer();
|
|
|
this.updateSessionPlayState();
|
|
|
this.updateWidgetsForStateChange();
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ this.updateAllForms();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -516,6 +611,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
this.saveCurrentState();
|
|
|
this.updateSessionPlayState();
|
|
|
await this.updateWidgetsForPlayStateChange();
|
|
|
+
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ await this.updateAllForms();
|
|
|
} catch (error) {
|
|
|
throw new Error(`Resume from pause failed: ${error}`);
|
|
|
}
|
|
|
@@ -645,6 +743,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
// 更新卡片显示暂停状态
|
|
|
await this.updateWidgetsForPlayStateChange();
|
|
|
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ await this.updateAllForms();
|
|
|
+
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService pause error: ${error}`);
|
|
|
}
|
|
|
@@ -780,6 +881,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
await this.updateWidgetsForSongChange(newSong, true);
|
|
|
}
|
|
|
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ await this.updateAllForms();
|
|
|
+
|
|
|
} catch (error) {
|
|
|
await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
|
|
|
} finally {
|
|
|
@@ -822,6 +926,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
this.stateModel.updateCurrentSong(currentSong);
|
|
|
}
|
|
|
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ await this.updateAllForms();
|
|
|
+
|
|
|
} catch (error) {
|
|
|
throw new Error;
|
|
|
} finally {
|
|
|
@@ -1444,6 +1551,21 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
LogUtils.getInstance().LOGI(`AVSession seek command failed: ${error}`);
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+ // 播放模式设置监听
|
|
|
+ avSession.on('setLoopMode', (mode: number) => {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession setLoopMode command received: ${mode}`);
|
|
|
+ try {
|
|
|
+ this.setPlayMode(mode);
|
|
|
+ // 更新AvSession播放状态以反映新的播放模式
|
|
|
+ this.updateSessionPlayState();
|
|
|
+ // 同步更新桌面卡片
|
|
|
+ this.updateAllForms();
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession setLoopMode command failed: ${error}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners set up successfully');
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set AVSession listeners: ${error}`);
|
|
|
@@ -1787,7 +1909,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
const playbackPosition = (duration - position < threshold) ? 0 : position;
|
|
|
const currentSong = this.playlistModel.getCurrentSong();
|
|
|
if (currentSong!=null) {
|
|
|
- PreferencesUtil.putSync(currentSong.filePath, playbackPosition);
|
|
|
+ PuraPreferencesUtil.putSync(currentSong.filePath, playbackPosition);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
@@ -1802,7 +1924,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
//
|
|
|
// }
|
|
|
if (song!= null){
|
|
|
- const position = PreferencesUtil.getNumberSync(song.filePath, 0);
|
|
|
+ const position: number = PuraPreferencesUtil.getNumberSync(song.filePath, 0);
|
|
|
if (position > 0) {
|
|
|
await this.playerManager.seekToPosition(position.toString());
|
|
|
}
|
|
|
@@ -2053,18 +2175,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
|
|
|
// ==================== StateChangeCallback 接口实现 ====================
|
|
|
|
|
|
- /**
|
|
|
- * 状态变化回调(来自StateSyncService的本地通知)
|
|
|
- */
|
|
|
- onStateChanged?(state: PlayerState): void {
|
|
|
- try {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Received state change notification - isPlaying=${state.isPlaying}`);
|
|
|
- // 这里可以处理来自其他组件的状态变化通知
|
|
|
- // 通常情况下,状态变化是由本服务发起的,所以这里主要用于调试和监控
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService onStateChanged error: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
* 歌曲变化回调(来自StateSyncService的本地通知)
|
|
|
@@ -2078,17 +2188,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 进度变化回调(来自StateSyncService的本地通知)
|
|
|
- */
|
|
|
- onProgressChanged?(progress: PlayProgress): void {
|
|
|
- try {
|
|
|
- // LogUtils.getInstance().LOGI(`UnifiedPlayerService: Received progress change notification - ${progress.percentage.toFixed(1)}%`);
|
|
|
- // 这里可以处理来自其他组件的进度变化通知
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService onProgressChanged error: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
// ==================== WidgetControlCallback 接口实现 ====================
|
|
|
|
|
|
@@ -2348,6 +2447,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
// 播放开始时更新卡片显示播放状态
|
|
|
this.updateWidgetsForPlayStateChange();
|
|
|
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ this.updateAllForms();
|
|
|
+
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: New song playback started successfully');
|
|
|
}
|
|
|
|
|
|
@@ -2363,6 +2465,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
// 更新卡片显示播放完成状态
|
|
|
this.updateWidgetsForPlayStateChange();
|
|
|
|
|
|
+ // 同步更新所有桌面卡片状态
|
|
|
+ this.updateAllForms();
|
|
|
+
|
|
|
// 异步处理自动播放逻辑,避免阻塞主线程
|
|
|
setTimeout(() => {
|
|
|
this.handleAutoPlayOnCompletion().catch(() => {
|
|
|
@@ -2520,10 +2625,170 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- updateAllForms() {
|
|
|
- let formData=this.getCurrentSong();
|
|
|
- let songState=this.getCurrentState();
|
|
|
- throw new Error('Method not implemented.');
|
|
|
+ /**
|
|
|
+ * 更新所有桌面卡片状态
|
|
|
+ * 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
|
|
|
+ */
|
|
|
+ async updateAllForms() {
|
|
|
+ try {
|
|
|
+ const now = Date.now();
|
|
|
+
|
|
|
+ // 防重复调用:如果正在更新或距离上次更新时间太近,则跳过
|
|
|
+ if (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;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从持久化存储获取所有formId
|
|
|
+ const context = AppStorage.get('context') as common.UIAbilityContext;
|
|
|
+ if (!context) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No context available');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const preferencesUtil: PreferencesUtil = PreferencesUtil.getInstance();
|
|
|
+ const prefs = await preferencesUtil.getPreferences(context);
|
|
|
+ const formIds: string[] = await preferencesUtil.getFormIds(prefs);
|
|
|
+
|
|
|
+ if (formIds.length === 0) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No form IDs found');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建卡片数据,包含VideoItem和PlayerState信息
|
|
|
+ const widgetPlayerState: WidgetPlayerState = {
|
|
|
+ isPlaying: songState.isPlaying || false,
|
|
|
+ isPaused: songState.isPaused || true,
|
|
|
+ isLoading: songState.isLoading || false,
|
|
|
+ currentPosition: songState.currentPosition || 0,
|
|
|
+ duration: songState.duration || 0,
|
|
|
+ hasNext: songState.hasNext || false,
|
|
|
+ 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)
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ // 平铺数据结构,确保桌面卡片能正确接收
|
|
|
+ const flatWidgetData: widgeData = {
|
|
|
+ // VideoItem数据平铺
|
|
|
+ id: formData.id || '',
|
|
|
+ name: formData.name || '',
|
|
|
+ artist: formData.artist || '',
|
|
|
+ album: formData.album || '',
|
|
|
+ pixelMapPath: formData.pixelMapPath || '',
|
|
|
+ duration: typeof formData.duration === 'string' ? parseInt(formData.duration || '0') : (formData.duration || 0),
|
|
|
+ filePath: formData.filePath || '',
|
|
|
+ imgName: '',
|
|
|
+ imageColorHex: '2A2A2A',
|
|
|
+
|
|
|
+ // PlayerState数据平铺
|
|
|
+ isPlaying: widgetPlayerState.isPlaying,
|
|
|
+ isPaused: widgetPlayerState.isPaused,
|
|
|
+ isLoading: widgetPlayerState.isLoading,
|
|
|
+ currentPosition: widgetPlayerState.currentPosition,
|
|
|
+ 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
|
|
|
+ };
|
|
|
+
|
|
|
+ // 记录更新统计
|
|
|
+ let successCount = 0;
|
|
|
+ let failedCount = 0;
|
|
|
+ const invalidFormIds: string[] = [];
|
|
|
+
|
|
|
+ // 逐个更新所有卡片
|
|
|
+ for (const formId of formIds) {
|
|
|
+ try {
|
|
|
+ const formBindingDataInstance = formBindingData.createFormBindingData(flatWidgetData);
|
|
|
+ await formProvider.updateForm(formId, formBindingDataInstance);
|
|
|
+ successCount++;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Updated form ${formId} - isPlaying=${flatWidgetData.isPlaying}, hasNext=${flatWidgetData.hasNext}, hasPrevious=${flatWidgetData.hasPrevious}`);
|
|
|
+ } catch (error) {
|
|
|
+ failedCount++;
|
|
|
+ const errorStr: string = error?.toString() || '';
|
|
|
+
|
|
|
+ // 检查是否是Form ID不存在的错误,并且增加更严格的判断条件
|
|
|
+ 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`);
|
|
|
+ } else {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to update form ${formId}: ${error} (not marking as invalid)`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动清理无效的Form ID
|
|
|
+ if (invalidFormIds.length > 0) {
|
|
|
+ try {
|
|
|
+ await preferencesUtil.removeFormIds(prefs, invalidFormIds);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Removed ${invalidFormIds.length} invalid form IDs: ${invalidFormIds.join(', ')}`);
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to clean invalid form IDs: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ // 重置更新标志
|
|
|
+ this.isUpdatingForms = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算播放进度百分比
|
|
|
+ */
|
|
|
+ private calculateProgressPercentage(current: number, total: number): number {
|
|
|
+ if (total <= 0) return 0;
|
|
|
+ return Math.min(100, Math.max(0, (current / total) * 100));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化时间显示
|
|
|
+ */
|
|
|
+ private formatTimeDisplay(seconds: number): string {
|
|
|
+ const mins = Math.floor(seconds / 60);
|
|
|
+ const secs = Math.floor(seconds % 60);
|
|
|
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2720,54 +2985,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 设置当前播放模式到AVSession
|
|
|
- */
|
|
|
- private setCurrentPlayMode(): void {
|
|
|
- try {
|
|
|
- if (!this.avSessionController) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const currentSong = this.playlistModel.getCurrentSong();
|
|
|
- if (!currentSong) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 修复duration转换问题 - 优先从播放器获取实际duration
|
|
|
- let duration = 0;
|
|
|
- try {
|
|
|
- const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
- if (ijkPlayer) {
|
|
|
- const playerDuration = ijkPlayer.getDuration();
|
|
|
- if (playerDuration > 0) {
|
|
|
- duration = playerDuration;
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using player duration: ${duration}ms`);
|
|
|
- } else {
|
|
|
- // 备用方案:解析VideoItem中的duration
|
|
|
- duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using parsed duration: ${duration}ms from '${currentSong.duration}'`);
|
|
|
- }
|
|
|
- } else {
|
|
|
- duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: No player available, using parsed duration: ${duration}ms`);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error getting player duration, using parsed: ${duration}ms, error: ${error}`);
|
|
|
- }
|
|
|
-
|
|
|
- // 获取歌词内容(如果有的话)
|
|
|
- const lyricContent = ''; // 这里可以根据需要获取歌词内容
|
|
|
-
|
|
|
- this.avSessionController.setAVMetadataMusic(currentSong, duration, lyricContent);
|
|
|
-
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession metadata updated for ${currentSong.name} with duration ${duration}ms`);
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set current play mode: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
* 更新AVSession元数据(带防抖和智能更新)
|