UnifiedPlayerService.ets 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537
  1. import { VideoItem } from '../../viewmodel/VideoItem';
  2. import { PlayerManager, IPlayerManager, PlayerStateCallback } from './PlayerManager';
  3. import { PlayerStateModel, PlayerState, PlayerStateListener, PlayMode, PlayerError, PlayerErrorType } from './PlayerStateModel';
  4. import { PlaylistModel } from './PlaylistModel';
  5. import { PlayProgress, WidgetData, WidgetSize, WidgetTheme } from '../widget/WidgetTypes';
  6. import { IjkMediaPlayer, LogUtils } from '@ohos/ijkplayer';
  7. import { common } from '@kit.AbilityKit';
  8. import { PreferencesUtil, StrUtil } from '@pura/harmony-utils';
  9. import { DataPersistenceService, PlaylistSyncService, PlaylistSyncListener, IDataPersistenceService, PlaylistData,
  10. PlayerStateData } from './DataPersistenceService';
  11. import { StateSyncService, IStateSyncService, StateChangeCallback, WidgetControlCallback } from './StateSyncService';
  12. import { ErrorRecoveryStrategy, IErrorRecoveryStrategy, ErrorContext, ErrorRecoveryAction } from './ErrorRecoveryStrategy';
  13. import { EnhancedFormUpdateService, UpdateStats } from '../widget/EnhancedFormUpdateService';
  14. import { AvSessionController } from '../../controller/AvSessionController';
  15. import { avSession } from '@kit.AVSessionKit';
  16. import json from '@ohos.util.json';
  17. /**
  18. * 服务就绪状态详情接口
  19. */
  20. export interface ServiceReadinessDetails {
  21. initialized: boolean;
  22. hasPlayerInstance: boolean;
  23. hasContext: boolean;
  24. dataRestored: boolean;
  25. playlistSize: number;
  26. currentIndex: number;
  27. }
  28. /**
  29. * 服务就绪信息接口
  30. */
  31. export interface ServiceReadinessInfo {
  32. isReady: boolean;
  33. details: ServiceReadinessDetails;
  34. }
  35. /**
  36. * 初始化验证结果接口
  37. */
  38. export interface InitializationValidation {
  39. isValid: boolean;
  40. errors: string[];
  41. warnings: string[];
  42. }
  43. /**
  44. * 统一播放器服务接口
  45. * 提供统一的播放控制接口,替代LocalMusic中的播放器逻辑
  46. */
  47. export interface IPlayerService {
  48. // 播放控制 - 基于LocalMusic现有方法
  49. startPlayOrResumePlay(): Promise<void>;
  50. pause(): Promise<void>;
  51. stop(): Promise<void>;
  52. seekTo(position: string): Promise<void>;
  53. // 播放列表控制 - 基于LocalMusic现有方法
  54. playNext(): Promise<void>;
  55. playPrevious(): Promise<void>;
  56. playSongAtIndex(index: number): Promise<void>;
  57. // 状态查询 - 兼容LocalMusic数据结构
  58. getCurrentState(): PlayerState;
  59. getCurrentSong(): VideoItem | null;
  60. getPlaylist(): VideoItem[];
  61. getCurrentIndex(): number;
  62. getCurrentPosition(): number;
  63. // 数据恢复状态
  64. isDataRestorationCompleted(): boolean;
  65. waitForDataRestoration(maxWaitMs?: number): Promise<boolean>;
  66. forceDataRestoration(): Promise<boolean>;
  67. // 服务就绪状态检查
  68. isAllServicesReady(): boolean;
  69. waitForAllServicesReady(maxWaitMs?: number, checkDataRestore?: boolean): Promise<boolean>;
  70. getServiceReadinessInfo(): ServiceReadinessInfo;
  71. validateInitialization(): InitializationValidation;
  72. // 播放模式控制 - 基于LocalMusic现有功能
  73. setPlayMode(mode: number): void;
  74. getPlayMode(): number;
  75. // 音量和速度控制 - 基于LocalMusic现有功能
  76. setVolume(volume: number): void;
  77. setPlaybackSpeed(speed: number): void;
  78. // 播放列表管理
  79. setPlaylist(songs: VideoItem[], currentIndex?: number): void;
  80. addToPlaylist(song: VideoItem, index?: number): void;
  81. removeFromPlaylist(index: number): VideoItem | null;
  82. // 事件监听
  83. addStateListener(listener: PlayerStateListener): void;
  84. removeStateListener(listener: PlayerStateListener): void;
  85. // 初始化和清理
  86. initialize(context: common.UIAbilityContext): Promise<void>;
  87. release(): void;
  88. }
  89. /**
  90. * 统一播放器服务实现
  91. * 整合PlayerManager、PlayerStateModel和PlaylistModel
  92. */
  93. export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, StateChangeCallback, WidgetControlCallback, PlayerStateCallback {
  94. private static instance: UnifiedPlayerService | null = null;
  95. private playerManager: IPlayerManager;
  96. private stateModel: PlayerStateModel;
  97. private playlistModel: PlaylistModel;
  98. private dataPersistence: IDataPersistenceService;
  99. private playlistSync: PlaylistSyncService;
  100. private stateSync: IStateSyncService;
  101. private errorRecovery: IErrorRecoveryStrategy;
  102. private widgetUpdateService: EnhancedFormUpdateService;
  103. private avSessionController: AvSessionController | null = null;
  104. private context: common.UIAbilityContext | null = null;
  105. private progressTimer: number = -1;
  106. private isInitialized: boolean = false;
  107. private isDataRestored: boolean = false; // 新增:数据恢复完成标识
  108. private isPlayerPreparedForCurrentSong: boolean = false; // 新增:播放器是否已为当前歌曲准备就绪
  109. private currentRetryCount: number = 0;
  110. private lastAvSessionUpdate: number = 0;
  111. private lastAvMetadataUpdate: number = 0; // 新增:上次元数据更新时间
  112. private avSessionUpdateTimer: number = -1; // 新增:系统播控更新防抖定时器
  113. private avMetadataUpdateTimer: number = -1; // 新增:元数据更新防抖定时器
  114. private constructor() {
  115. this.playerManager = PlayerManager.getInstance();
  116. this.stateModel = new PlayerStateModel();
  117. this.playlistModel = new PlaylistModel();
  118. this.dataPersistence = DataPersistenceService.getInstance();
  119. this.playlistSync = PlaylistSyncService.getInstance();
  120. this.stateSync = StateSyncService.getInstance();
  121. this.errorRecovery = ErrorRecoveryStrategy.getInstance();
  122. this.widgetUpdateService = EnhancedFormUpdateService.getInstance();
  123. LogUtils.getInstance().LOGI('UnifiedPlayerService: Instance created');
  124. }
  125. public static getInstance(): UnifiedPlayerService {
  126. if (!UnifiedPlayerService.instance) {
  127. UnifiedPlayerService.instance = new UnifiedPlayerService();
  128. }
  129. return UnifiedPlayerService.instance;
  130. }
  131. async initialize(context: common.UIAbilityContext): Promise<void> {
  132. if (this.isInitialized && this.context === context) {
  133. LogUtils.getInstance().LOGI('UnifiedPlayerService已经初始化过了');
  134. return;
  135. }
  136. try {
  137. console.log("Heanup2 UnifiedPlayerService: 开始快速初始化服务");
  138. this.context = context;
  139. // 快速初始化核心组件
  140. this.playerManager.setStateCallback(this);
  141. this.widgetUpdateService.setAppContext(context);
  142. // 设置同步监听器
  143. this.playlistSync.addSyncListener(this);
  144. this.stateSync.subscribeToStateChanges(this);
  145. this.stateSync.subscribeToWidgetControl(this);
  146. // 设置状态模型监听器,自动广播状态变化
  147. class StateListenerImpl implements PlayerStateListener {
  148. private stateSync: IStateSyncService;
  149. private unifiedService: UnifiedPlayerService;
  150. constructor(stateSync: IStateSyncService, unifiedService: UnifiedPlayerService) {
  151. this.stateSync = stateSync;
  152. this.unifiedService = unifiedService;
  153. }
  154. onStateChanged(state: PlayerState): void {
  155. this.stateSync.broadcastState(state);
  156. // 使用防抖机制更新系统播控,避免频繁更新
  157. this.unifiedService.updateSessionPlayState();
  158. // 状态变化时更新卡片
  159. this.unifiedService.updateWidgetsForStateChange();
  160. }
  161. onSongChanged(song: VideoItem): void {
  162. this.stateSync.broadcastSongChange(song);
  163. // 使用防抖机制更新系统播控元数据,避免频繁更新
  164. this.unifiedService.updateAvSessionMetadata(song);
  165. // 移除重复的播放状态更新,让onPlaybackStarted处理
  166. // 歌曲变化时更新卡片
  167. this.unifiedService.updateWidgetsForSongChange(song);
  168. }
  169. onProgressChanged(progress: PlayProgress): void {
  170. this.stateSync.broadcastProgress(progress);
  171. // 进度变化时更新卡片(防抖处理)
  172. this.unifiedService.updateWidgetsForProgressChange(progress);
  173. }
  174. onError(error: PlayerError): void {
  175. LogUtils.getInstance().LOGI(`Player error: ${error.message}`);
  176. }
  177. }
  178. const stateListener = new StateListenerImpl(this.stateSync, this);
  179. this.stateModel.addStateListener(stateListener);
  180. this.setupProgressTimer();
  181. this.isInitialized = true;
  182. // 异步初始化耗时组件,避免阻塞
  183. this.initializeHeavyComponentsAsync(context);
  184. LogUtils.getInstance().LOGI('UnifiedPlayerService: Core initialization completed');
  185. } catch (error) {
  186. LogUtils.getInstance().LOGI(`UnifiedPlayerService initialization error: ${error}`);
  187. throw new Error(`初始化失败: ${error}`);
  188. }
  189. }
  190. /**
  191. * 异步初始化耗时组件
  192. */
  193. private initializeHeavyComponentsAsync(context: common.UIAbilityContext): void {
  194. setTimeout(async () => {
  195. try {
  196. console.log("Heanup2 UnifiedPlayerService: 开始异步初始化耗时组件");
  197. // 并行初始化各个服务以提高效率
  198. const initPromises = [
  199. this.playerManager.initialize(context),
  200. this.dataPersistence.initialize(context),
  201. this.playlistSync.initialize(context),
  202. this.stateSync.initialize(context)
  203. ];
  204. await Promise.all(initPromises);
  205. // 立即尝试快速恢复关键数据(从AppStorage)
  206. await this.fastRestoreFromMemory();
  207. this.initializeAvSession();
  208. // 完整的数据恢复(从持久化存储)
  209. await this.restorePersistedState();
  210. console.log("Heanup2 UnifiedPlayerService: 异步组件初始化完成");
  211. LogUtils.getInstance().LOGI('UnifiedPlayerService: Heavy components initialized');
  212. } catch (error) {
  213. console.log(`Heanup2 UnifiedPlayerService: 异步组件初始化失败: ${error}`);
  214. LogUtils.getInstance().LOGI(`UnifiedPlayerService heavy components init error: ${error}`);
  215. // 即使失败也设置数据恢复完成,避免无限等待
  216. this.isDataRestored = true;
  217. }
  218. }, 10); // 缩短延迟,让数据恢复更快开始
  219. }
  220. // 播放控制方法
  221. async startPlayOrResumePlay(): Promise<void> {
  222. try {
  223. console.log("Heanup2 UnifiedPlayerService: 开始恢复播放");
  224. // 统一的服务就绪检查 - 这是最核心的检查
  225. if (!this.isAllServicesReady()) {
  226. console.log("Heanup2 UnifiedPlayerService: 服务未就绪,等待服务初始化完成");
  227. const isReady = await this.waitForAllServicesReady(3000, false);
  228. if (!isReady) {
  229. const error = new Error('Services not ready for playback');
  230. await this.handlePlaybackError(error, PlayerErrorType.INITIALIZATION_ERROR);
  231. return;
  232. }
  233. }
  234. // 获取当前歌曲 - 如果没有就尝试恢复
  235. let currentSong = this.playlistModel.getCurrentSong();
  236. if (!currentSong) {
  237. console.log("Heanup2 UnifiedPlayerService: 没有可播放的歌曲,尝试恢复持久化状态");
  238. await this.restorePersistedState();
  239. currentSong = this.playlistModel.getCurrentSong();
  240. if (!currentSong) {
  241. console.log("Heanup2 UnifiedPlayerService: 恢复后仍然没有可播放的歌曲");
  242. await this.handlePlaybackError(new Error('No song to play'), PlayerErrorType.PLAYBACK_ERROR);
  243. return;
  244. }
  245. console.log("Heanup2 UnifiedPlayerService: 恢复成功,当前歌曲信息:"+json.stringify(currentSong));
  246. }
  247. this.stateModel.updateLoadingState(true);
  248. // 简化的播放器状态检查 - 检查是否可以从暂停恢复
  249. const ijkPlayer = this.playerManager.getIjkPlayer()!; // 服务就绪后播放器一定存在
  250. const shouldResumeFromPause = this.canResumeFromPause(ijkPlayer);
  251. if (shouldResumeFromPause) {
  252. console.log("Heanup2 UnifiedPlayerService: 从暂停状态恢复播放");
  253. return await this.resumeFromPause();
  254. }
  255. // 新歌播放前的必要检查
  256. await this.validateSongForPlayback(currentSong);
  257. // 设置播放器并开始播放
  258. await this.setupAndStartPlayback(currentSong);
  259. } catch (error) {
  260. this.stateModel.updateLoadingState(false);
  261. this.stateModel.updatePlayingState(false);
  262. await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
  263. }
  264. }
  265. /**
  266. * 检查是否可以从暂停状态恢复播放
  267. */
  268. private canResumeFromPause(ijkPlayer: IjkMediaPlayer): boolean {
  269. const isPausedByManager = this.playerManager.isPausedState();
  270. const isCurrentlyPlaying = ijkPlayer.isPlaying();
  271. const playerDuration = ijkPlayer.getDuration();
  272. const hasValidMedia = playerDuration > 0;
  273. // 如果认为是暂停但没有有效媒体,清除假暂停状态
  274. if (isPausedByManager && !hasValidMedia) {
  275. console.log("Heanup2 UnifiedPlayerService: 检测到假暂停状态,清除暂停标记");
  276. this.playerManager.clearPausedState();
  277. this.isPlayerPreparedForCurrentSong = false;
  278. return false;
  279. }
  280. // 如果播放器正在播放,同步状态
  281. if (isCurrentlyPlaying) {
  282. this.syncPlayingState();
  283. return false; // 不需要恢复,已经在播放
  284. }
  285. // 检查是否可以从暂停恢复
  286. const canResume = isPausedByManager &&
  287. !isCurrentlyPlaying &&
  288. hasValidMedia &&
  289. this.isPlayerPreparedForCurrentSong;
  290. console.log(`Heanup2 UnifiedPlayerService: 暂停恢复检查 - canResume: ${canResume}`);
  291. return canResume;
  292. }
  293. /**
  294. * 同步播放状态
  295. */
  296. private syncPlayingState(): void {
  297. console.log("Heanup2 UnifiedPlayerService: 播放器正在播放,同步状态");
  298. this.stateModel.updateLoadingState(false);
  299. this.stateModel.updatePlayingState(true);
  300. this.playerManager.clearPausedState();
  301. this.startProgressTimer();
  302. this.updateSessionPlayState();
  303. this.updateWidgetsForStateChange();
  304. }
  305. /**
  306. * 从暂停状态恢复播放
  307. */
  308. private async resumeFromPause(): Promise<void> {
  309. try {
  310. console.log("Heanup2 UnifiedPlayerService: 直接从暂停状态恢复播放");
  311. this.stateModel.updateLoadingState(false);
  312. await this.playerManager.startPlayOrResumePlay();
  313. this.stateModel.updatePlayingState(true);
  314. this.startProgressTimer();
  315. setTimeout(() => this.updateProgress(), 100);
  316. this.saveCurrentState();
  317. this.updateSessionPlayState();
  318. await this.updateWidgetsForPlayStateChange();
  319. } catch (error) {
  320. throw new Error(`Resume from pause failed: ${error}`);
  321. }
  322. }
  323. /**
  324. * 验证歌曲是否可以播放
  325. */
  326. private async validateSongForPlayback(song: VideoItem): Promise<void> {
  327. // 检查文件是否存在(对于本地文件)
  328. if (!song.filePath.startsWith('http')) {
  329. const fileExists = await this.checkFileExists(song.filePath);
  330. if (!fileExists) {
  331. throw new Error(`File not found: ${song.filePath}`);
  332. }
  333. } else {
  334. // 检查网络连接(对于网络资源)
  335. const networkAvailable = await this.checkNetworkConnection();
  336. if (!networkAvailable) {
  337. throw new Error('Network not available');
  338. }
  339. }
  340. }
  341. /**
  342. * 设置播放器并开始播放
  343. */
  344. private async setupAndStartPlayback(song: VideoItem): Promise<void> {
  345. console.log(`Heanup2 UnifiedPlayerService: 设置播放器并开始播放: ${song.name}`);
  346. // 设置播放源和配置
  347. await this.setupPlayerForSong(song);
  348. // 准备播放器(实际播放会在 onPrepared 回调中开始)
  349. const preparedPlayer = this.playerManager.getIjkPlayer();
  350. if (preparedPlayer) {
  351. preparedPlayer.prepareAsync();
  352. }
  353. // 立即更新基础元数据(使用解析的duration),后续在播放器准备好后会用实际duration更新
  354. await this.updateAvSessionMetadata(song);
  355. this.restorePlaybackPosition(song);
  356. this.currentRetryCount = 0;
  357. }
  358. /**
  359. * 直接播放新歌曲,避免恢复逻辑干扰
  360. * 专为 playNext/playPrevious 设计
  361. */
  362. private async playNewSongDirectly(song: VideoItem | null): Promise<void> {
  363. if (!song) {
  364. throw new Error('No song provided for direct play');
  365. }
  366. this.stateModel.updateLoadingState(true);
  367. const playerInstance = this.playerManager.getIjkPlayer();
  368. if (!playerInstance) {
  369. throw new Error('Player not initialized');
  370. }
  371. // 检查文件是否存在(对于本地文件)
  372. if (!song.filePath.startsWith('http')) {
  373. const fileExists = await this.checkFileExists(song.filePath);
  374. if (!fileExists) {
  375. throw new Error(`File not found: ${song.filePath}`);
  376. }
  377. }
  378. // 设置播放源和配置
  379. await this.setupPlayerForSong(song);
  380. // 准备播放器
  381. const preparedPlayer = this.playerManager.getIjkPlayer();
  382. if (preparedPlayer) {
  383. preparedPlayer.prepareAsync();
  384. // 注意:实际播放和状态更新会在 onPrepared 回调中开始
  385. }
  386. // 重置重试计数
  387. this.currentRetryCount = 0;
  388. }
  389. async pause(): Promise<void> {
  390. try {
  391. // 检查服务是否已准备就绪
  392. if (!this.isAllServicesReady()) {
  393. LogUtils.getInstance().LOGI('UnifiedPlayerService: pause - 服务未就绪');
  394. // 对于暂停操作,如果服务未就绪,可以尝试简单的状态更新
  395. this.stateModel.updatePlayingState(false);
  396. return;
  397. }
  398. const ijkPlayer = this.playerManager.getIjkPlayer();
  399. // 保存当前播放位置
  400. this.savePlaybackPosition();
  401. // 暂停播放器 - 确保先暂停播放器
  402. this.playerManager.pausePlayback();
  403. // 立即更新状态模型为暂停状态,确保状态同步
  404. this.stateModel.updatePlayingState(false);
  405. // 停止进度定时器
  406. this.stopProgressTimer();
  407. // 等待一小段时间确保播放器状态稳定
  408. await new Promise<void>(resolve => setTimeout(resolve, 50));
  409. // 保存播放状态变化
  410. this.saveCurrentState();
  411. this.updateSessionPlayState();
  412. // 更新卡片显示暂停状态
  413. await this.updateWidgetsForPlayStateChange();
  414. } catch (error) {
  415. LogUtils.getInstance().LOGI(`UnifiedPlayerService pause error: ${error}`);
  416. }
  417. }
  418. async stop(): Promise<void> {
  419. try {
  420. this.savePlaybackPosition();
  421. this.playerManager.stopPlayback();
  422. this.stateModel.updatePlayingState(false);
  423. this.stateModel.updateProgress(0, 0);
  424. this.stopProgressTimer();
  425. } catch (error) {
  426. LogUtils.getInstance().LOGI(`UnifiedPlayerService stop error: ${error}`);
  427. throw new Error;
  428. }
  429. }
  430. /**
  431. * 静默停止播放,不触发状态变化通知(用于歌曲切换)
  432. */
  433. private async stopSilently(): Promise<void> {
  434. try {
  435. this.savePlaybackPosition();
  436. this.playerManager.stopPlayback();
  437. this.stopProgressTimer();
  438. // 不调用 updatePlayingState,避免触发状态变化通知
  439. } catch (error) {
  440. LogUtils.getInstance().LOGI(`UnifiedPlayerService stopSilently error: ${error}`);
  441. throw new Error;
  442. }
  443. }
  444. async seekTo(position: string): Promise<void> {
  445. try {
  446. const currentSong = this.playlistModel.getCurrentSong();
  447. if (!currentSong) {
  448. throw new Error('No song to seek');
  449. }
  450. // 检查是否支持拖动(wma格式不支持)
  451. if (StrUtil.isNotEmpty(currentSong.filePath) &&
  452. currentSong.filePath.toLowerCase().endsWith('.wma')) {
  453. throw new Error('WMA format does not support seeking');
  454. }
  455. await this.playerManager.seekToPosition(position);
  456. } catch (error) {
  457. throw new Error(`Failed to seek to position: ${error}`);
  458. }
  459. }
  460. // 播放列表控制方法
  461. async playNext(): Promise<void> {
  462. try {
  463. // 首先检查服务是否已准备就绪
  464. if (!this.isAllServicesReady()) {
  465. LogUtils.getInstance().LOGI('UnifiedPlayerService: playNext - 服务未就绪,等待服务初始化完成');
  466. const isReady = await this.waitForAllServicesReady(2000, false);
  467. if (!isReady) {
  468. const error = new Error('Services not ready for playNext');
  469. await this.handlePlaybackError(error, PlayerErrorType.INITIALIZATION_ERROR);
  470. return;
  471. }
  472. }
  473. const playMode = this.stateModel.getState().playMode;
  474. if (!this.playlistModel.hasNext(playMode)) {
  475. return;
  476. }
  477. const moved = this.playlistModel.moveToNext(playMode);
  478. if (!moved) {
  479. return;
  480. }
  481. // 更新状态模型的索引
  482. const newIndex = this.playlistModel.getCurrentIndex();
  483. const newSong = this.playlistModel.getCurrentSong();
  484. this.stateModel.updateCurrentIndex(newIndex);
  485. if (newSong) {
  486. this.stateModel.updateCurrentSong(newSong);
  487. }
  488. // 静默停止当前播放(不触发状态变化通知)
  489. await this.stopSilently();
  490. // 直接播放新歌曲,不调用通用的恢复逻辑
  491. await this.playNewSongDirectly(newSong);
  492. // 更新状态模型中的播放列表状态
  493. this.updatePlaylistStateInModel();
  494. // 强制更新卡片显示新歌曲(重要事件)
  495. if (newSong) {
  496. await this.updateWidgetsForSongChange(newSong, true);
  497. }
  498. } catch (error) {
  499. await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
  500. }
  501. }
  502. async playPrevious(): Promise<void> {
  503. try {
  504. // 首先检查服务是否已准备就绪
  505. if (!this.isAllServicesReady()) {
  506. LogUtils.getInstance().LOGI('UnifiedPlayerService: playPrevious - 服务未就绪,等待服务初始化完成');
  507. const isReady = await this.waitForAllServicesReady(2000, false);
  508. if (!isReady) {
  509. const error = new Error('Services not ready for playPrevious');
  510. await this.handlePlaybackError(error, PlayerErrorType.INITIALIZATION_ERROR);
  511. return;
  512. }
  513. }
  514. const playMode = this.stateModel.getState().playMode;
  515. if (!this.playlistModel.hasPrevious(playMode)) {
  516. return;
  517. }
  518. // 先获取当前歌曲信息用于日志
  519. const currentSong = this.playlistModel.getCurrentSong();
  520. const currentIndex = this.playlistModel.getCurrentIndex();
  521. const moved = this.playlistModel.moveToPrevious(playMode);
  522. if (!moved) {
  523. return;
  524. }
  525. // 更新状态模型的索引
  526. const newIndex = this.playlistModel.getCurrentIndex();
  527. const newSong = this.playlistModel.getCurrentSong();
  528. this.stateModel.updateCurrentIndex(newIndex);
  529. if (newSong) {
  530. this.stateModel.updateCurrentSong(newSong);
  531. }
  532. // 静默停止当前播放(不触发状态变化通知)
  533. await this.stopSilently();
  534. // 直接播放新歌曲,不调用通用的恢复逻辑
  535. await this.playNewSongDirectly(newSong);
  536. // 更新状态模型中的播放列表状态
  537. this.updatePlaylistStateInModel();
  538. // 强制更新卡片显示新歌曲(重要事件)
  539. if (newSong) {
  540. await this.updateWidgetsForSongChange(newSong, true);
  541. }
  542. } catch (error) {
  543. await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
  544. }
  545. }
  546. async playSongAtIndex(index: number): Promise<void> {
  547. try {
  548. const success = this.playlistModel.playSongAtIndex(index);
  549. if (!success) {
  550. throw new Error(`Invalid song index: ${index}`);
  551. }
  552. this.stateModel.updateCurrentIndex(index);
  553. // 静默停止当前播放并开始新歌曲(不触发状态变化通知)
  554. await this.stopSilently();
  555. await this.startPlayOrResumePlay();
  556. // 更新状态模型中的播放列表状态
  557. this.updatePlaylistStateInModel();
  558. // 通知歌曲变化
  559. const currentSong = this.playlistModel.getCurrentSong();
  560. if (currentSong) {
  561. this.stateModel.updateCurrentSong(currentSong);
  562. }
  563. } catch (error) {
  564. throw new Error;
  565. }
  566. }
  567. // 状态查询方法
  568. getCurrentState(): PlayerState {
  569. return this.stateModel.getState();
  570. }
  571. getCurrentSong(): VideoItem | null {
  572. return this.playlistModel.getCurrentSong();
  573. }
  574. getPlaylist(): VideoItem[] {
  575. return this.playlistModel.getSongs();
  576. }
  577. getCurrentIndex(): number {
  578. return this.playlistModel.getCurrentIndex();
  579. }
  580. /**
  581. * 检查数据是否已从持久化存储恢复
  582. */
  583. isDataRestorationCompleted(): boolean {
  584. return this.isDataRestored;
  585. }
  586. /**
  587. * 等待数据恢复完成
  588. */
  589. async waitForDataRestoration(maxWaitMs: number = 3000): Promise<boolean> {
  590. const startTime = Date.now();
  591. while (!this.isDataRestored && (Date.now() - startTime) < maxWaitMs) {
  592. await new Promise<void>(resolve => setTimeout(resolve, 100));
  593. }
  594. return this.isDataRestored;
  595. }
  596. /**
  597. * 强制触发数据恢复(用于桌面卡片冷启动场景)
  598. */
  599. async forceDataRestoration(): Promise<boolean> {
  600. try {
  601. LogUtils.getInstance().LOGI('UnifiedPlayerService: Force data restoration started');
  602. // 如果数据已经恢复,直接返回
  603. if (this.isDataRestored) {
  604. LogUtils.getInstance().LOGI('UnifiedPlayerService: Data already restored');
  605. return true;
  606. }
  607. // 强制执行数据恢复
  608. await this.restorePersistedState();
  609. // 如果播放列表仍为空,尝试从内存快速恢复
  610. if (this.playlistModel.getTotalCount() === 0) {
  611. LogUtils.getInstance().LOGI('UnifiedPlayerService: Playlist empty, trying memory restore');
  612. await this.fastRestoreFromMemory();
  613. }
  614. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Force restoration completed, playlist size: ${this.playlistModel.getTotalCount()}`);
  615. return this.isDataRestored;
  616. } catch (error) {
  617. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Force data restoration failed: ${error}`);
  618. return false;
  619. }
  620. }
  621. /**
  622. * 检查所有相关服务是否已准备就绪
  623. * 用于桌面卡片控制等场景,确保在执行播放控制前服务状态正常
  624. */
  625. isAllServicesReady(): boolean {
  626. try {
  627. // 检查基本初始化状态
  628. if (!this.isInitialized) {
  629. LogUtils.getInstance().LOGI('UnifiedPlayerService: Service not initialized');
  630. return false;
  631. }
  632. // 检查核心组件是否就绪
  633. if (!this.playerManager || !this.stateModel || !this.playlistModel) {
  634. LogUtils.getInstance().LOGI('UnifiedPlayerService: Core components not ready');
  635. return false;
  636. }
  637. // 检查 PlayerManager 是否已经初始化
  638. const playerInstance = this.playerManager.getIjkPlayer();
  639. if (!playerInstance) {
  640. LogUtils.getInstance().LOGI('UnifiedPlayerService: Player instance not ready');
  641. return false;
  642. }
  643. // 检查上下文是否可用
  644. if (!this.context) {
  645. LogUtils.getInstance().LOGI('UnifiedPlayerService: Context not available');
  646. return false;
  647. }
  648. LogUtils.getInstance().LOGI('UnifiedPlayerService: All services ready');
  649. return true;
  650. } catch (error) {
  651. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Service readiness check failed: ${error}`);
  652. return false;
  653. }
  654. }
  655. /**
  656. * 验证初始化完整性
  657. * 在应用启动后调用,确保所有关键组件都已正确初始化
  658. */
  659. validateInitialization(): InitializationValidation {
  660. const errors: string[] = [];
  661. const warnings: string[] = [];
  662. try {
  663. // 检查基础初始化
  664. if (!this.isInitialized) {
  665. errors.push('Service not initialized');
  666. }
  667. // 检查核心组件
  668. if (!this.playerManager) {
  669. errors.push('PlayerManager not available');
  670. } else {
  671. const playerInstance = this.playerManager.getIjkPlayer();
  672. if (!playerInstance) {
  673. warnings.push('Player instance not ready (may initialize later)');
  674. }
  675. }
  676. if (!this.stateModel) {
  677. errors.push('StateModel not available');
  678. }
  679. if (!this.playlistModel) {
  680. errors.push('PlaylistModel not available');
  681. }
  682. if (!this.context) {
  683. warnings.push('Context not set (may be set later)');
  684. }
  685. // 检查数据持久化服务
  686. if (!this.dataPersistence) {
  687. errors.push('DataPersistence service not available');
  688. }
  689. // 检查同步服务
  690. if (!this.stateSync) {
  691. errors.push('StateSync service not available');
  692. }
  693. if (!this.playlistSync) {
  694. errors.push('PlaylistSync service not available');
  695. }
  696. // 检查卡片更新服务
  697. if (!this.widgetUpdateService) {
  698. warnings.push('WidgetUpdate service not available');
  699. }
  700. // 检查错误恢复策略
  701. if (!this.errorRecovery) {
  702. warnings.push('ErrorRecovery strategy not available');
  703. }
  704. const isValid = errors.length === 0;
  705. LogUtils.getInstance().LOGI(`UnifiedPlayerService validation: ${isValid ? 'VALID' : 'INVALID'}, errors: ${errors.length}, warnings: ${warnings.length}`);
  706. const result: InitializationValidation = {
  707. isValid,
  708. errors,
  709. warnings
  710. };
  711. return result;
  712. } catch (error) {
  713. errors.push(`Validation failed with exception: ${error}`);
  714. const result: InitializationValidation = {
  715. isValid: false,
  716. errors,
  717. warnings
  718. };
  719. return result;
  720. }
  721. }
  722. /**
  723. * 获取服务就绪状态的详细信息
  724. * 用于调试和用户反馈
  725. */
  726. getServiceReadinessInfo(): ServiceReadinessInfo {
  727. const playerInstance = this.playerManager?.getIjkPlayer();
  728. const playlistSize = this.playlistModel?.getTotalCount() || 0;
  729. const currentIndex = this.playlistModel?.getCurrentIndex() || -1;
  730. const details: ServiceReadinessDetails = {
  731. initialized: this.isInitialized,
  732. hasPlayerInstance: !!playerInstance,
  733. hasContext: !!this.context,
  734. dataRestored: this.isDataRestored,
  735. playlistSize: playlistSize,
  736. currentIndex: currentIndex
  737. };
  738. const isReady = this.isAllServicesReady();
  739. LogUtils.getInstance().LOGI(`UnifiedPlayerService readiness: ${JSON.stringify(details)}`);
  740. const result: ServiceReadinessInfo = { isReady, details };
  741. return result;
  742. }
  743. /**
  744. * 等待所有服务准备就绪
  745. * @param maxWaitMs 最大等待时间(毫秒)
  746. * @param checkDataRestore 是否同时等待数据恢复完成
  747. */
  748. async waitForAllServicesReady(maxWaitMs: number = 5000, checkDataRestore: boolean = true): Promise<boolean> {
  749. const startTime = Date.now();
  750. const checkInterval = 100; // 每100ms检查一次
  751. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Waiting for services ready (maxWait: ${maxWaitMs}ms, checkDataRestore: ${checkDataRestore})`);
  752. while ((Date.now() - startTime) < maxWaitMs) {
  753. // 检查基础服务就绪状态
  754. if (this.isAllServicesReady()) {
  755. // 如果不需要检查数据恢复,直接返回成功
  756. if (!checkDataRestore) {
  757. LogUtils.getInstance().LOGI('UnifiedPlayerService: All services ready (data restore check skipped)');
  758. return true;
  759. }
  760. // 检查数据恢复状态
  761. if (this.isDataRestored) {
  762. LogUtils.getInstance().LOGI('UnifiedPlayerService: All services ready and data restored');
  763. return true;
  764. }
  765. // 检查播放列表是否有数据(即使数据恢复标识未设置)
  766. const playlistSize = this.playlistModel.getTotalCount();
  767. if (playlistSize > 0) {
  768. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Services ready with playlist data (${playlistSize} songs)`);
  769. return true;
  770. }
  771. }
  772. // 短暂等待后继续检查
  773. await new Promise<void>(resolve => setTimeout(resolve, checkInterval));
  774. }
  775. // 超时检查最终状态
  776. const finalCheck = this.isAllServicesReady();
  777. const playlistSize = this.playlistModel?.getTotalCount() || 0;
  778. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Service readiness timeout - finalCheck: ${finalCheck}, playlistSize: ${playlistSize}, dataRestored: ${this.isDataRestored}`);
  779. return finalCheck;
  780. }
  781. // 播放模式控制
  782. setPlayMode(mode: number): void {
  783. const playMode = mode as PlayMode;
  784. this.stateModel.updatePlayMode(playMode);
  785. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Play mode set to ${mode}`);
  786. }
  787. getPlayMode(): number {
  788. return this.stateModel.getState().playMode;
  789. }
  790. // 音量和速度控制
  791. setVolume(volume: number): void {
  792. const volumeStr = volume.toString();
  793. this.playerManager.setVolume(volumeStr, volumeStr);
  794. this.stateModel.updateVolume(volume);
  795. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Volume set to ${volume}`);
  796. }
  797. setPlaybackSpeed(speed: number): void {
  798. const speedStr = speed.toString() + 'f';
  799. this.playerManager.setPlaybackSpeed(speedStr);
  800. this.stateModel.updateSpeed(speed);
  801. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Speed set to ${speed}`);
  802. }
  803. // 播放列表管理
  804. setPlaylist(songs: VideoItem[], currentIndex: number = 0): void {
  805. // 记录调用栈信息以便调试
  806. const stack = new Error().stack || 'No stack available';
  807. const caller = stack.split('\n')[2] || 'Unknown caller';
  808. this.playlistModel.replaceSongs(songs, currentIndex);
  809. this.stateModel.updateCurrentIndex(currentIndex);
  810. // 更新状态模型中的播放列表状态
  811. this.updatePlaylistStateInModel();
  812. // 更新当前歌曲
  813. const currentSong = this.playlistModel.getCurrentSong();
  814. this.stateModel.updateCurrentSong(currentSong);
  815. // 同步到持久化存储
  816. this.syncPlaylistToStorage();
  817. // 更新卡片显示播放列表变化
  818. this.updateWidgetsForPlaylistChange();
  819. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Playlist set with ${songs.length} songs, caller: ${caller.trim()}`);
  820. // 如果设置的是小播放列表,记录更多信息
  821. if (songs.length <= 20) {
  822. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Small playlist detected, caller stack: ${stack.substring(0, 500)}`);
  823. }
  824. }
  825. addToPlaylist(song: VideoItem, index?: number): void {
  826. this.playlistModel.addSong(song, index);
  827. // 更新状态模型中的播放列表状态
  828. this.updatePlaylistStateInModel();
  829. // 同步到持久化存储
  830. this.syncPlaylistToStorage();
  831. // 更新卡片显示播放列表变化
  832. this.updateWidgetsForPlaylistChange();
  833. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Song added to playlist`);
  834. }
  835. removeFromPlaylist(index: number): VideoItem | null {
  836. const removedSong = this.playlistModel.removeSong(index);
  837. if (removedSong) {
  838. this.stateModel.updateCurrentIndex(this.playlistModel.getCurrentIndex());
  839. // 更新状态模型中的播放列表状态
  840. this.updatePlaylistStateInModel();
  841. // 更新当前歌曲
  842. const currentSong = this.playlistModel.getCurrentSong();
  843. this.stateModel.updateCurrentSong(currentSong);
  844. // 同步到持久化存储
  845. this.syncPlaylistToStorage();
  846. // 更新卡片显示播放列表变化
  847. this.updateWidgetsForPlaylistChange();
  848. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Song removed from playlist at index ${index}`);
  849. }
  850. return removedSong;
  851. }
  852. // 事件监听
  853. addStateListener(listener: PlayerStateListener): void {
  854. this.stateModel.addStateListener(listener);
  855. }
  856. removeStateListener(listener: PlayerStateListener): void {
  857. this.stateModel.removeStateListener(listener);
  858. }
  859. // 清理资源
  860. release(): void {
  861. try {
  862. this.stopProgressTimer();
  863. // 清理防抖定时器
  864. if (this.avSessionUpdateTimer !== -1) {
  865. clearTimeout(this.avSessionUpdateTimer);
  866. this.avSessionUpdateTimer = -1;
  867. }
  868. if (this.avMetadataUpdateTimer !== -1) {
  869. clearTimeout(this.avMetadataUpdateTimer);
  870. this.avMetadataUpdateTimer = -1;
  871. }
  872. // 保存当前状态
  873. this.saveCurrentState();
  874. if (this.avSessionController) {
  875. this.avSessionController.unregisterSessionListener();
  876. this.avSessionController = null;
  877. }
  878. // 清理同步监听器
  879. this.playlistSync.removeSyncListener(this);
  880. this.playlistSync.release();
  881. this.stateSync.unsubscribeFromStateChanges(this);
  882. this.stateSync.unsubscribeFromWidgetControl(this);
  883. this.stateSync.release();
  884. this.playerManager.release();
  885. this.playlistModel.clear();
  886. this.isInitialized = false;
  887. LogUtils.getInstance().LOGI('UnifiedPlayerService: Resources released');
  888. } catch (error) {
  889. LogUtils.getInstance().LOGI(`UnifiedPlayerService release error: ${error}`);
  890. }
  891. }
  892. /**
  893. * 初始化AVSession
  894. * 按照官方文档的要求:创建 -> 注册控制命令 -> 设置元数据 -> 激活
  895. */
  896. private initializeAvSession(): void {
  897. try {
  898. this.avSessionController = AvSessionController.getInstance(false);
  899. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller created');
  900. setTimeout(() => {
  901. this.setAvSessionListener();
  902. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners configured');
  903. }, 500);
  904. } catch (error) {
  905. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to initialize AVSession: ${error}`);
  906. }
  907. }
  908. /**
  909. * 设置AVSession监听器
  910. * 注意:控制命令已在AvSessionController中注册,这里设置实际的处理逻辑
  911. */
  912. private setAvSessionListener(): void {
  913. if (!this.avSessionController) {
  914. return;
  915. }
  916. const avSession = this.avSessionController.getAvSession();
  917. if (!avSession) {
  918. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession not available for listener setup');
  919. return;
  920. }
  921. try {
  922. // 播放事件监听
  923. avSession.on('play', () => {
  924. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession play command received');
  925. this.startPlayOrResumePlay().catch((error: Error) => {
  926. LogUtils.getInstance().LOGI(`AVSession play command failed: ${error}`);
  927. });
  928. });
  929. // 暂停事件监听
  930. avSession.on('pause', () => {
  931. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession pause command received');
  932. this.pause().catch((error: Error) => {
  933. LogUtils.getInstance().LOGI(`AVSession pause command failed: ${error}`);
  934. });
  935. });
  936. // 停止事件监听
  937. avSession.on('stop', () => {
  938. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession stop command received');
  939. this.stop().catch((error: Error) => {
  940. LogUtils.getInstance().LOGI(`AVSession stop command failed: ${error}`);
  941. });
  942. });
  943. // 下一首事件监听
  944. avSession.on('playNext', () => {
  945. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession playNext command received');
  946. this.playNext().catch((error: Error) => {
  947. LogUtils.getInstance().LOGI(`AVSession playNext command failed: ${error}`);
  948. });
  949. });
  950. // 上一首事件监听
  951. avSession.on('playPrevious', () => {
  952. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession playPrevious command received');
  953. this.playPrevious().catch((error: Error) => {
  954. LogUtils.getInstance().LOGI(`AVSession playPrevious command failed: ${error}`);
  955. });
  956. });
  957. // 拖拽进度事件监听
  958. avSession.on('seek', (time: number) => {
  959. LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession seek command received: ${time}ms`);
  960. this.seekTo(time.toString()).catch((error: Error) => {
  961. LogUtils.getInstance().LOGI(`AVSession seek command failed: ${error}`);
  962. });
  963. });
  964. // 循环模式设置监听
  965. avSession.on('setLoopMode', (mode: avSession.LoopMode) => {
  966. LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession setLoopMode command received: ${mode}`);
  967. const playMode = this.convertLoopModeToPlayMode(mode);
  968. this.setPlayMode(playMode);
  969. });
  970. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners set up successfully');
  971. } catch (error) {
  972. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set AVSession listeners: ${error}`);
  973. }
  974. }
  975. /**
  976. * 转换AVSession循环模式为播放模式
  977. */
  978. private convertLoopModeToPlayMode(loopMode: avSession.LoopMode): number {
  979. switch (loopMode) {
  980. case avSession.LoopMode.LOOP_MODE_SINGLE:
  981. return PlayMode.SINGLE_REPEAT;
  982. case avSession.LoopMode.LOOP_MODE_LIST:
  983. return PlayMode.NORMAL;
  984. case avSession.LoopMode.LOOP_MODE_SHUFFLE:
  985. return PlayMode.RANDOM;
  986. case avSession.LoopMode.LOOP_MODE_SEQUENCE:
  987. default:
  988. return PlayMode.SEQUENCE;
  989. }
  990. }
  991. // ==================== 错误处理和恢复方法 ====================
  992. /**
  993. * 处理播放错误
  994. */
  995. private async handlePlaybackError(error: Error, errorType: PlayerErrorType = PlayerErrorType.PLAYBACK_ERROR): Promise<void> {
  996. try {
  997. // 创建播放器错误对象
  998. const playerError = new PlayerError(errorType, error.message);
  999. playerError.retryCount = this.currentRetryCount;
  1000. // 创建错误上下文
  1001. const context: ErrorContext = {
  1002. currentSong: this.playlistModel.getCurrentSong(),
  1003. playlist: this.playlistModel.getSongs(),
  1004. currentIndex: this.playlistModel.getCurrentIndex(),
  1005. retryCount: this.currentRetryCount,
  1006. isNetworkAvailable: true // 这里可以实际检查网络状态
  1007. };
  1008. // 获取恢复策略
  1009. const recoveryAction = await this.errorRecovery.handleError(playerError, context);
  1010. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error recovery action: ${recoveryAction}`);
  1011. // 执行恢复动作
  1012. await this.executeRecoveryAction(recoveryAction, playerError, context);
  1013. } catch (recoveryError) {
  1014. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error recovery failed: ${recoveryError}`);
  1015. this.stateModel.notifyError(new PlayerError(PlayerErrorType.PLAYBACK_ERROR, `Recovery failed: ${recoveryError.message}`));
  1016. }
  1017. }
  1018. /**
  1019. * 执行错误恢复动作
  1020. */
  1021. private async executeRecoveryAction(action: ErrorRecoveryAction, error: PlayerError, context: ErrorContext): Promise<void> {
  1022. switch (action) {
  1023. case ErrorRecoveryAction.RETRY:
  1024. await this.retryCurrentOperation(error);
  1025. break;
  1026. case ErrorRecoveryAction.SKIP_TO_NEXT:
  1027. await this.skipToNextSong();
  1028. break;
  1029. case ErrorRecoveryAction.STOP_PLAYBACK:
  1030. await this.stop();
  1031. this.stateModel.notifyError(error);
  1032. break;
  1033. case ErrorRecoveryAction.WAIT_AND_RETRY:
  1034. await this.waitAndRetry(error);
  1035. break;
  1036. case ErrorRecoveryAction.SHOW_ERROR:
  1037. this.stateModel.notifyError(error);
  1038. break;
  1039. default:
  1040. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Unknown recovery action: ${action}`);
  1041. this.stateModel.notifyError(error);
  1042. }
  1043. }
  1044. /**
  1045. * 重试当前操作
  1046. */
  1047. private async retryCurrentOperation(error: PlayerError): Promise<void> {
  1048. try {
  1049. this.currentRetryCount++;
  1050. const delay = this.errorRecovery.getRetryDelay(this.currentRetryCount);
  1051. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Retrying operation in ${delay}ms (attempt ${this.currentRetryCount})`);
  1052. // 等待指定时间后重试
  1053. setTimeout(async () => {
  1054. try {
  1055. await this.startPlayOrResumePlay();
  1056. this.currentRetryCount = 0; // 重试成功,重置计数
  1057. } catch (retryError) {
  1058. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Retry failed: ${retryError}`);
  1059. await this.handlePlaybackError(retryError as Error, error.type);
  1060. }
  1061. }, delay);
  1062. } catch (retryError) {
  1063. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Retry setup failed: ${retryError}`);
  1064. await this.skipToNextSong();
  1065. }
  1066. }
  1067. /**
  1068. * 跳到下一首歌曲
  1069. */
  1070. private async skipToNextSong(): Promise<void> {
  1071. try {
  1072. LogUtils.getInstance().LOGI('UnifiedPlayerService: Skipping to next song due to error');
  1073. // 重置重试计数
  1074. this.currentRetryCount = 0;
  1075. // 检查是否有下一首
  1076. const playMode = this.stateModel.getState().playMode;
  1077. if (this.playlistModel.hasNext(playMode)) {
  1078. await this.playNext();
  1079. } else {
  1080. // 没有下一首,停止播放
  1081. await this.stop();
  1082. LogUtils.getInstance().LOGI('UnifiedPlayerService: No next song available, stopping playback');
  1083. }
  1084. } catch (skipError) {
  1085. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to skip to next song: ${skipError}`);
  1086. await this.stop();
  1087. }
  1088. }
  1089. /**
  1090. * 等待后重试
  1091. */
  1092. private async waitAndRetry(error: PlayerError): Promise<void> {
  1093. try {
  1094. this.currentRetryCount++;
  1095. const delay = this.errorRecovery.getRetryDelay(this.currentRetryCount);
  1096. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Waiting ${delay}ms before retry (network error)`);
  1097. // 更新状态为加载中
  1098. this.stateModel.updateLoadingState(true);
  1099. setTimeout(async () => {
  1100. try {
  1101. await this.startPlayOrResumePlay();
  1102. this.currentRetryCount = 0; // 重试成功,重置计数
  1103. } catch (retryError) {
  1104. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Network retry failed: ${retryError}`);
  1105. await this.handlePlaybackError(retryError as Error, PlayerErrorType.NETWORK_ERROR);
  1106. }
  1107. }, delay);
  1108. } catch (waitError) {
  1109. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Wait and retry setup failed: ${waitError}`);
  1110. await this.skipToNextSong();
  1111. }
  1112. }
  1113. /**
  1114. * 检查文件是否存在
  1115. */
  1116. private async checkFileExists(filePath: string): Promise<boolean> {
  1117. try {
  1118. // 这里可以添加文件存在性检查逻辑
  1119. // 暂时返回true,实际实现需要使用文件系统API
  1120. return true;
  1121. } catch (error) {
  1122. LogUtils.getInstance().LOGI(`UnifiedPlayerService: File check failed: ${error}`);
  1123. return false;
  1124. }
  1125. }
  1126. /**
  1127. * 检查网络连接
  1128. */
  1129. private async checkNetworkConnection(): Promise<boolean> {
  1130. try {
  1131. // 这里可以添加网络连接检查逻辑
  1132. // 暂时返回true,实际实现需要使用网络API
  1133. return true;
  1134. } catch (error) {
  1135. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Network check failed: ${error}`);
  1136. return false;
  1137. }
  1138. }
  1139. // 私有辅助方法
  1140. /**
  1141. * 检查播放器是否需要重新设置
  1142. */
  1143. private async needsPlayerSetup(song: VideoItem): Promise<boolean> {
  1144. try {
  1145. const ijkPlayer = this.playerManager.getIjkPlayer();
  1146. if (!ijkPlayer) {
  1147. console.log("Heanup2 UnifiedPlayerService: 播放器不存在,需要设置");
  1148. return true;
  1149. }
  1150. // 检查播放器是否有数据源
  1151. const duration = ijkPlayer.getDuration();
  1152. if (duration <= 0) {
  1153. console.log("Heanup2 UnifiedPlayerService: 播放器无数据源或时长无效,需要设置");
  1154. return true;
  1155. }
  1156. // 检查当前数据源是否匹配(这里我们通过检查当前歌曲信息来判断)
  1157. const currentSong = this.playlistModel.getCurrentSong();
  1158. if (!currentSong || currentSong.filePath !== song.filePath) {
  1159. console.log(`Heanup2 UnifiedPlayerService: 歌曲文件路径不匹配,需要重新设置 (当前: ${currentSong?.filePath}, 目标: ${song.filePath})`);
  1160. return true;
  1161. }
  1162. console.log(`Heanup2 UnifiedPlayerService: 播放器已设置正确的数据源,无需重新设置 (时长: ${duration}ms)`);
  1163. return false;
  1164. } catch (error) {
  1165. console.log(`Heanup2 UnifiedPlayerService: 检查播放器设置状态时出错: ${error},默认需要设置`);
  1166. return true;
  1167. }
  1168. }
  1169. private async setupPlayerForSong(song: VideoItem): Promise<void> {
  1170. const ijkPlayer = this.playerManager.getIjkPlayer();
  1171. if (!ijkPlayer) {
  1172. throw new Error('Player not initialized');
  1173. }
  1174. console.log("Heanup2 UnifiedPlayerService: 开始重新设置播放器,这会重置所有状态");
  1175. // 标记播放器未准备(因为要重新设置)
  1176. this.isPlayerPreparedForCurrentSong = false;
  1177. // 重置播放器 - 这会清除所有状态包括播放位置
  1178. ijkPlayer.reset();
  1179. // 重新设置音频模式 - 重置后需要重新设置
  1180. ijkPlayer.setAudioId('unifiedPlayer');
  1181. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Reset audio mode for ${song.name}`);
  1182. // 重新设置配置
  1183. this.playerManager.setupIjkPlayerOptions();
  1184. // 设置音量(针对dsf格式特殊处理)
  1185. const volume = this.stateModel.getState().volume;
  1186. if (song.filePath.toLowerCase().endsWith('.dsf')) {
  1187. this.playerManager.setVolume('1', '1');
  1188. } else {
  1189. this.playerManager.setVolume(volume.toString(), volume.toString());
  1190. }
  1191. // 设置数据源
  1192. ijkPlayer.setDataSource(song.filePath);
  1193. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Set data source to ${song.filePath}`);
  1194. // 设置HTTP请求头
  1195. const headers = new Map([
  1196. ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (bbcallen@gmail.com)"],
  1197. ["referer", "https://www.bilibili.com"]
  1198. ]);
  1199. ijkPlayer.setDataSourceHeader(headers);
  1200. // 设置播放速度
  1201. const speed = this.stateModel.getState().speed;
  1202. this.playerManager.setPlaybackSpeed(speed.toString() + 'f');
  1203. // 设置消息监听器(重要:用于处理播放器内部事件)
  1204. ijkPlayer.setMessageListener();
  1205. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player setup for song ${song.name}`);
  1206. console.log("Heanup2 UnifiedPlayerService: 播放器重新设置完成,准备开始新的播放");
  1207. }
  1208. private setupProgressTimer(): void {
  1209. this.progressTimer = setInterval(() => {
  1210. this.updateProgress();
  1211. }, 1000); // 每秒更新一次进度
  1212. }
  1213. private startProgressTimer(): void {
  1214. // 先停止现有的定时器,避免重复启动
  1215. this.stopProgressTimer();
  1216. // 启动新的定时器
  1217. this.setupProgressTimer();
  1218. LogUtils.getInstance().LOGI('UnifiedPlayerService: Progress timer started');
  1219. }
  1220. private stopProgressTimer(): void {
  1221. if (this.progressTimer !== -1) {
  1222. clearInterval(this.progressTimer);
  1223. this.progressTimer = -1;
  1224. LogUtils.getInstance().LOGI('UnifiedPlayerService: Progress timer stopped');
  1225. }
  1226. }
  1227. private updateProgress(): void {
  1228. try {
  1229. const ijkPlayer = this.playerManager.getIjkPlayer();
  1230. const currentState = this.stateModel.getState();
  1231. // 只有在播放器存在且正在播放时才更新进度
  1232. if (!ijkPlayer || !currentState.isPlaying) {
  1233. return;
  1234. }
  1235. const currentPosition = ijkPlayer.getCurrentPosition();
  1236. const duration = ijkPlayer.getDuration();
  1237. // 确保获取到有效的位置信息
  1238. if (currentPosition >= 0 && duration > 0) {
  1239. this.stateModel.updateProgress(currentPosition, duration);
  1240. this.updateSessionPlayState();
  1241. }
  1242. } catch (error) {
  1243. LogUtils.getInstance().LOGI(`UnifiedPlayerService updateProgress error: ${error}`);
  1244. }
  1245. }
  1246. getCurrentPosition(): number {
  1247. try {
  1248. const ijkPlayer = this.playerManager.getIjkPlayer();
  1249. if (!ijkPlayer) {
  1250. return 0;
  1251. }
  1252. return ijkPlayer.getCurrentPosition();
  1253. } catch (error) {
  1254. LogUtils.getInstance().LOGI(`UnifiedPlayerService getCurrentPosition error: ${error}`);
  1255. return 0;
  1256. }
  1257. }
  1258. private savePlaybackPosition(): void {
  1259. const ijkPlayer = this.playerManager.getIjkPlayer();
  1260. if (ijkPlayer!= null){
  1261. const position = ijkPlayer.getCurrentPosition();
  1262. // ToastUtil.showToast('保存记忆播放 = ' + position)
  1263. const duration = ijkPlayer.getDuration();
  1264. const threshold = 5000; // 阈值,单位为毫秒(这里设为5秒)
  1265. // 如果播放位置接近视频末尾,则保存 position 为 0
  1266. const playbackPosition = (duration - position < threshold) ? 0 : position;
  1267. const currentSong = this.playlistModel.getCurrentSong();
  1268. if (currentSong!=null) {
  1269. PreferencesUtil.putSync(currentSong.filePath, playbackPosition);
  1270. }
  1271. }
  1272. }
  1273. private async restorePlaybackPosition(song: VideoItem): Promise<void> {
  1274. // const position: number = PreferencesUtil.getNumberSync(this.videoUrl, 0);
  1275. // // ToastUtil.showToast('position = ' + position)
  1276. // if (this.mIjkMediaPlayer != null && position > 0) {
  1277. // // ToastUtil.showToast('seekTo = ' + position)
  1278. // this.seekTo(position + "");
  1279. //
  1280. // }
  1281. if (song!= null){
  1282. const position = PreferencesUtil.getNumberSync(song.filePath, 0);
  1283. if (position > 0) {
  1284. await this.playerManager.seekToPosition(position.toString());
  1285. }
  1286. }
  1287. // try {
  1288. // // 使用DataPersistenceService恢复播放进度
  1289. // const progressData = await this.dataPersistence.loadPlaybackProgress(song.filePath);
  1290. // if (progressData && progressData.position > 0 && !progressData.completed) {
  1291. // await this.playerManager.seekToPosition(progressData.position.toString());
  1292. // LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restored playback position ${progressData.position}ms for ${song.name}`);
  1293. // }
  1294. // } catch (error) {
  1295. // LogUtils.getInstance().LOGI(`UnifiedPlayerService restorePlaybackPosition error: ${error}`);
  1296. // }
  1297. }
  1298. // ==================== 数据持久化和同步方法 ====================
  1299. /**
  1300. * 恢复持久化的状态
  1301. */
  1302. /**
  1303. * 快速从内存中恢复关键数据(AppStorage)
  1304. */
  1305. private async fastRestoreFromMemory(): Promise<void> {
  1306. try {
  1307. console.log("Heanup2 UnifiedPlayerService: 开始从内存快速恢复关键数据");
  1308. // 从AppStorage快速获取播放列表数据
  1309. const playlistData = AppStorage.get<PlaylistData>('player_playlist');
  1310. if (playlistData && playlistData.songs.length > 0) {
  1311. console.log(`Heanup2 UnifiedPlayerService: 从内存快速恢复播放列表,歌曲数量=${playlistData.songs.length}`);
  1312. this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
  1313. this.stateModel.updateCurrentIndex(playlistData.currentIndex);
  1314. this.stateModel.updatePlayMode(playlistData.playMode);
  1315. // 恢复当前歌曲
  1316. const currentSong = this.playlistModel.getCurrentSong();
  1317. if (currentSong) {
  1318. this.stateModel.updateCurrentSong(currentSong);
  1319. console.log(`Heanup2 UnifiedPlayerService: 快速恢复当前歌曲: ${currentSong.name}`);
  1320. }
  1321. }
  1322. // 从AppStorage快速获取播放状态数据
  1323. const stateData = AppStorage.get<PlayerStateData>('player_state') as PlayerStateData;
  1324. if (stateData) {
  1325. console.log(`Heanup2 UnifiedPlayerService: 从内存快速恢复播放状态 - playing: ${stateData.isPlaying}, index: ${stateData.currentIndex}`);
  1326. this.stateModel.updateVolume(stateData.volume);
  1327. this.stateModel.updateSpeed(stateData.speed);
  1328. this.stateModel.updatePlayMode(stateData.playMode);
  1329. this.stateModel.updatePlayingState(stateData.isPlaying || false);
  1330. if (stateData.currentIndex >= 0) {
  1331. this.stateModel.updateCurrentIndex(stateData.currentIndex);
  1332. }
  1333. }
  1334. console.log("Heanup2 UnifiedPlayerService: 内存数据快速恢复完成");
  1335. // 设置一个临时的数据恢复完成标识,允许快速响应
  1336. this.isDataRestored = true;
  1337. } catch (error) {
  1338. console.log(`Heanup2 UnifiedPlayerService: 快速恢复内存数据失败: ${error}`);
  1339. }
  1340. }
  1341. private async restorePersistedState(): Promise<void> {
  1342. try {
  1343. console.log("Heanup2 UnifiedPlayerService: 开始快速恢复持久化状态");
  1344. // 并行加载播放列表和播放状态,提高效率
  1345. const loadResults = await Promise.all([
  1346. this.dataPersistence.loadPlaylist().catch(() => null),
  1347. this.dataPersistence.loadPlayerState().catch(() => null)
  1348. ]);
  1349. const playlistData = loadResults[0];
  1350. const stateData = loadResults[1];
  1351. // 恢复播放列表
  1352. if (playlistData && playlistData.songs.length > 0) {
  1353. console.log(`Heanup2 UnifiedPlayerService: 恢复播放列表,歌曲数量=${playlistData.songs.length}, 当前索引=${playlistData.currentIndex}`);
  1354. this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
  1355. this.stateModel.updateCurrentIndex(playlistData.currentIndex);
  1356. this.stateModel.updatePlayMode(playlistData.playMode);
  1357. // 恢复当前歌曲到状态模型
  1358. const currentSong = this.playlistModel.getCurrentSong();
  1359. if (currentSong) {
  1360. this.stateModel.updateCurrentSong(currentSong);
  1361. console.log(`Heanup2 UnifiedPlayerService: 恢复当前歌曲: ${currentSong.name}`);
  1362. }
  1363. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restored playlist with ${playlistData.songs.length} songs`);
  1364. } else {
  1365. console.log("Heanup2 UnifiedPlayerService: 没有找到保存的播放列表或播放列表为空");
  1366. }
  1367. // 恢复播放状态
  1368. if (stateData) {
  1369. console.log(`Heanup2 UnifiedPlayerService: 加载到的播放状态 - playing: ${stateData.isPlaying}, paused: ${stateData.isPaused}, index: ${stateData.currentIndex}`);
  1370. // 批量更新状态,减少回调次数
  1371. this.stateModel.updateVolume(stateData.volume);
  1372. this.stateModel.updateSpeed(stateData.speed);
  1373. this.stateModel.updatePlayMode(stateData.playMode);
  1374. // 恢复播放状态
  1375. this.stateModel.updatePlayingState(stateData.isPlaying || false);
  1376. // 恢复当前播放索引
  1377. if (stateData.currentIndex >= 0) {
  1378. this.stateModel.updateCurrentIndex(stateData.currentIndex);
  1379. }
  1380. console.log("Heanup2 UnifiedPlayerService: 播放状态恢复完成");
  1381. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restored player state - playing: ${stateData.isPlaying}, paused: ${stateData.isPaused}, index: ${stateData.currentIndex}`);
  1382. } else {
  1383. console.log("Heanup2 UnifiedPlayerService: 没有找到保存的播放状态");
  1384. }
  1385. console.log("Heanup2 UnifiedPlayerService: 持久化状态快速恢复完成");
  1386. // 设置数据恢复完成标识
  1387. this.isDataRestored = true;
  1388. LogUtils.getInstance().LOGI('UnifiedPlayerService: Data restoration completed');
  1389. } catch (error) {
  1390. console.log(`Heanup2 UnifiedPlayerService: 恢复持久化状态时出错: ${error}`);
  1391. LogUtils.getInstance().LOGI(`UnifiedPlayerService restorePersistedState error: ${error}`);
  1392. // 即使出错也设置为已恢复,避免无限等待
  1393. this.isDataRestored = true;
  1394. }
  1395. }
  1396. /**
  1397. * 保存当前状态
  1398. */
  1399. private saveCurrentState(): void {
  1400. try {
  1401. // 保存播放状态
  1402. const currentState = this.stateModel.getState();
  1403. this.dataPersistence.savePlayerState(currentState)
  1404. // 保存播放进度
  1405. this.savePlaybackPosition();
  1406. LogUtils.getInstance().LOGI('UnifiedPlayerService: Current state saved');
  1407. } catch (error) {
  1408. LogUtils.getInstance().LOGI(`UnifiedPlayerService saveCurrentState error: ${error}`);
  1409. }
  1410. }
  1411. /**
  1412. * 同步播放列表到存储
  1413. */
  1414. private syncPlaylistToStorage(): void {
  1415. try {
  1416. const playlist = this.playlistModel.getSongs();
  1417. const currentIndex = this.playlistModel.getCurrentIndex();
  1418. const playMode = this.stateModel.getState().playMode;
  1419. this.playlistSync.syncPlaylistToStorage(playlist, currentIndex, playMode);
  1420. } catch (error) {
  1421. LogUtils.getInstance().LOGI(`UnifiedPlayerService syncPlaylistToStorage error: ${error}`);
  1422. }
  1423. }
  1424. // ==================== PlaylistSyncListener 接口实现 ====================
  1425. /**
  1426. * 播放列表同步完成回调
  1427. */
  1428. onPlaylistSynced(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void {
  1429. try {
  1430. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Playlist synced - ${playlist.length} songs, index ${currentIndex}`);
  1431. // 播放列表同步完成,无需额外通知
  1432. } catch (error) {
  1433. LogUtils.getInstance().LOGI(`UnifiedPlayerService onPlaylistSynced error: ${error}`);
  1434. }
  1435. }
  1436. /**
  1437. * 播放列表加载完成回调
  1438. */
  1439. onPlaylistLoaded(playlistData: PlaylistData): void {
  1440. try {
  1441. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Playlist loaded from storage - ${playlistData.songs.length} songs`);
  1442. // 检查是否需要更新本地播放列表
  1443. const currentPlaylist = this.playlistModel.getSongs();
  1444. const currentIndex = this.playlistModel.getCurrentIndex();
  1445. // 如果存储的数据更新,更新本地播放列表
  1446. if (playlistData.songs.length !== currentPlaylist.length ||
  1447. playlistData.currentIndex !== currentIndex) {
  1448. this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
  1449. this.stateModel.updateCurrentIndex(playlistData.currentIndex);
  1450. this.stateModel.updatePlayMode(playlistData.playMode);
  1451. LogUtils.getInstance().LOGI('UnifiedPlayerService: Local playlist updated from storage');
  1452. }
  1453. // 更新数据恢复状态(如果还没有恢复的话)
  1454. if (!this.isDataRestored) {
  1455. this.isDataRestored = true;
  1456. LogUtils.getInstance().LOGI('UnifiedPlayerService: Data restoration completed via onPlaylistLoaded');
  1457. }
  1458. } catch (error) {
  1459. LogUtils.getInstance().LOGI(`UnifiedPlayerService onPlaylistLoaded error: ${error}`);
  1460. }
  1461. }
  1462. /**
  1463. * 自动同步执行回调
  1464. */
  1465. onAutoSyncPerformed(): void {
  1466. try {
  1467. // 定期保存当前状态
  1468. this.saveCurrentState();
  1469. } catch (error) {
  1470. LogUtils.getInstance().LOGI(`UnifiedPlayerService onAutoSyncPerformed error: ${error}`);
  1471. }
  1472. }
  1473. /**
  1474. * 同步错误回调
  1475. */
  1476. onSyncError(error: Error): void {
  1477. LogUtils.getInstance().LOGI(`UnifiedPlayerService sync error: ${error.message}`);
  1478. }
  1479. // ==================== 公共数据访问方法 ====================
  1480. /**
  1481. * 获取数据持久化服务实例
  1482. */
  1483. getDataPersistenceService(): IDataPersistenceService {
  1484. return this.dataPersistence;
  1485. }
  1486. /**
  1487. * 获取播放列表同步服务实例
  1488. */
  1489. getPlaylistSyncService(): PlaylistSyncService {
  1490. return this.playlistSync;
  1491. }
  1492. // ==================== StateChangeCallback 接口实现 ====================
  1493. /**
  1494. * 状态变化回调(来自StateSyncService的本地通知)
  1495. */
  1496. onStateChanged?(state: PlayerState): void {
  1497. try {
  1498. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Received state change notification - isPlaying=${state.isPlaying}`);
  1499. // 这里可以处理来自其他组件的状态变化通知
  1500. // 通常情况下,状态变化是由本服务发起的,所以这里主要用于调试和监控
  1501. } catch (error) {
  1502. LogUtils.getInstance().LOGI(`UnifiedPlayerService onStateChanged error: ${error}`);
  1503. }
  1504. }
  1505. /**
  1506. * 歌曲变化回调(来自StateSyncService的本地通知)
  1507. */
  1508. onSongChanged?(song: VideoItem): void {
  1509. try {
  1510. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Received song change notification - ${song.name}`);
  1511. // 这里可以处理来自其他组件的歌曲变化通知
  1512. } catch (error) {
  1513. LogUtils.getInstance().LOGI(`UnifiedPlayerService onSongChanged error: ${error}`);
  1514. }
  1515. }
  1516. /**
  1517. * 进度变化回调(来自StateSyncService的本地通知)
  1518. */
  1519. onProgressChanged?(progress: PlayProgress): void {
  1520. try {
  1521. // LogUtils.getInstance().LOGI(`UnifiedPlayerService: Received progress change notification - ${progress.percentage.toFixed(1)}%`);
  1522. // 这里可以处理来自其他组件的进度变化通知
  1523. } catch (error) {
  1524. LogUtils.getInstance().LOGI(`UnifiedPlayerService onProgressChanged error: ${error}`);
  1525. }
  1526. }
  1527. // ==================== WidgetControlCallback 接口实现 ====================
  1528. /**
  1529. * 处理来自卡片的播放/暂停命令
  1530. */
  1531. async onPlayPause?(): Promise<void> {
  1532. try {
  1533. LogUtils.getInstance().LOGI('UnifiedPlayerService: Received play/pause command from widget');
  1534. // 优先检查播放器的实际状态,而不是状态模型
  1535. const ijkPlayer = this.playerManager.getIjkPlayer();
  1536. const isActuallyPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
  1537. const stateModelPlaying = this.getCurrentState().isPlaying;
  1538. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widget playPause - StateModel: ${stateModelPlaying}, ActualPlayer: ${isActuallyPlaying}`);
  1539. // 如果播放器实际在播放,则暂停;否则开始/恢复播放
  1540. if (isActuallyPlaying) {
  1541. LogUtils.getInstance().LOGI('UnifiedPlayerService: Player is actually playing, will pause');
  1542. await this.pause();
  1543. } else {
  1544. LogUtils.getInstance().LOGI('UnifiedPlayerService: Player is not playing, will start/resume play');
  1545. await this.startPlayOrResumePlay();
  1546. }
  1547. LogUtils.getInstance().LOGI('UnifiedPlayerService: Play/pause command executed successfully');
  1548. } catch (error) {
  1549. LogUtils.getInstance().LOGI(`UnifiedPlayerService onPlayPause error: ${error}`);
  1550. throw new Error;
  1551. }
  1552. }
  1553. /**
  1554. * 处理来自卡片的下一首命令
  1555. */
  1556. async onNextSong?(): Promise<void> {
  1557. try {
  1558. LogUtils.getInstance().LOGI('UnifiedPlayerService: Received next song command from widget');
  1559. await this.playNext();
  1560. LogUtils.getInstance().LOGI('UnifiedPlayerService: Next song command executed successfully');
  1561. } catch (error) {
  1562. LogUtils.getInstance().LOGI(`UnifiedPlayerService onNextSong error: ${error}`);
  1563. throw new Error;
  1564. }
  1565. }
  1566. /**
  1567. * 处理来自卡片的上一首命令
  1568. */
  1569. async onPreviousSong?(): Promise<void> {
  1570. try {
  1571. LogUtils.getInstance().LOGI('UnifiedPlayerService: Received previous song command from widget');
  1572. await this.playPrevious();
  1573. LogUtils.getInstance().LOGI('UnifiedPlayerService: Previous song command executed successfully');
  1574. } catch (error) {
  1575. LogUtils.getInstance().LOGI(`UnifiedPlayerService onPreviousSong error: ${error}`);
  1576. throw new Error;
  1577. }
  1578. }
  1579. /**
  1580. * 处理来自卡片的拖动进度命令
  1581. */
  1582. async onSeekTo?(position: number): Promise<void> {
  1583. try {
  1584. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Received seek to command from widget - position: ${position}`);
  1585. await this.seekTo(position.toString());
  1586. LogUtils.getInstance().LOGI('UnifiedPlayerService: Seek to command executed successfully');
  1587. } catch (error) {
  1588. LogUtils.getInstance().LOGI(`UnifiedPlayerService onSeekTo error: ${error}`);
  1589. throw new Error;
  1590. }
  1591. }
  1592. /**
  1593. * 处理来自卡片的状态请求命令
  1594. */
  1595. async onStateRequest?(): Promise<void> {
  1596. try {
  1597. LogUtils.getInstance().LOGI('UnifiedPlayerService: Received state request from widget');
  1598. await this.broadcastCurrentState();
  1599. LogUtils.getInstance().LOGI('UnifiedPlayerService: State request handled successfully');
  1600. } catch (error) {
  1601. LogUtils.getInstance().LOGI(`UnifiedPlayerService onStateRequest error: ${error}`);
  1602. throw new Error;
  1603. }
  1604. }
  1605. // ==================== 状态同步服务访问方法 ====================
  1606. /**
  1607. * 获取状态同步服务实例
  1608. */
  1609. getStateSyncService(): IStateSyncService {
  1610. return this.stateSync;
  1611. }
  1612. /**
  1613. * 手动广播当前状态(用于响应卡片的状态请求)
  1614. */
  1615. async broadcastCurrentState(): Promise<void> {
  1616. try {
  1617. const currentState = this.getCurrentState();
  1618. await this.stateSync.broadcastState(currentState);
  1619. LogUtils.getInstance().LOGI('UnifiedPlayerService: Current state broadcasted manually');
  1620. } catch (error) {
  1621. LogUtils.getInstance().LOGI(`UnifiedPlayerService broadcastCurrentState error: ${error}`);
  1622. throw new Error;
  1623. }
  1624. }
  1625. // ==================== 私有辅助方法 ====================
  1626. /**
  1627. * 更新状态模型中的播放列表状态
  1628. */
  1629. private updatePlaylistStateInModel(): void {
  1630. try {
  1631. const currentIndex = this.playlistModel.getCurrentIndex();
  1632. const totalCount = this.playlistModel.getTotalCount();
  1633. const playMode = this.stateModel.getState().playMode;
  1634. const hasNext = this.playlistModel.hasNext(playMode);
  1635. const hasPrevious = this.playlistModel.hasPrevious(playMode);
  1636. this.stateModel.updatePlaylistState(hasNext, hasPrevious, totalCount);
  1637. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Playlist state updated - hasNext=${hasNext}, hasPrevious=${hasPrevious}, totalCount=${totalCount}`);
  1638. } catch (error) {
  1639. LogUtils.getInstance().LOGI(`UnifiedPlayerService updatePlaylistStateInModel error: ${error}`);
  1640. }
  1641. }
  1642. // ==================== PlayerStateCallback 实现 ====================
  1643. onPrepared(): void {
  1644. LogUtils.getInstance().LOGI('UnifiedPlayerService: Player prepared');
  1645. this.stateModel.updateLoadingState(false);
  1646. // 标记播放器已为当前歌曲准备就绪
  1647. this.isPlayerPreparedForCurrentSong = true;
  1648. console.log("Heanup2 UnifiedPlayerService: onPrepared - 播放器已为当前歌曲准备就绪");
  1649. setTimeout(() => {
  1650. const ijkPlayer = this.playerManager.getIjkPlayer();
  1651. const isActuallyPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
  1652. console.log(`Heanup2 UnifiedPlayerService: onPrepared - 实际播放状态: ${isActuallyPlaying}`);
  1653. // 强制同步状态模型
  1654. this.stateModel.updatePlayingState(isActuallyPlaying);
  1655. // 只触发状态同步,让onPlaybackStarted处理系统播控更新
  1656. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession state updated after player prepared');
  1657. }, 100);
  1658. // 播放器准备完成时更新卡片(移除加载状态)
  1659. this.updateWidgetsForStateChange();
  1660. }
  1661. onPlaybackStarted(): void {
  1662. LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback started (new song)');
  1663. // 这个回调只应该在新歌曲开始播放时被调用(通过onPrepared触发)
  1664. // 不应该在暂停恢复时被调用
  1665. // 立即更新播放状态
  1666. this.stateModel.updatePlayingState(true);
  1667. // 启动进度定时器
  1668. this.startProgressTimer();
  1669. // 保存播放状态变化
  1670. this.saveCurrentState();
  1671. // 延迟更新系统播控,避免与其他更新冲突
  1672. setTimeout(() => {
  1673. // 使用防抖机制更新系统播控(主要更新播放状态)
  1674. this.updateSessionPlayState();
  1675. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession state updated for new song playback');
  1676. }, 600); // 增加延迟,避免与onPrepared的更新冲突
  1677. // 播放开始时更新卡片显示播放状态
  1678. this.updateWidgetsForPlayStateChange();
  1679. LogUtils.getInstance().LOGI('UnifiedPlayerService: New song playback started successfully');
  1680. }
  1681. onPlaybackCompleted(): void {
  1682. LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback completed');
  1683. this.stateModel.updatePlayingState(false);
  1684. this.stopProgressTimer();
  1685. // 保存播放状态变化
  1686. this.saveCurrentState();
  1687. this.updateSessionPlayState();
  1688. // 更新卡片显示播放完成状态
  1689. this.updateWidgetsForPlayStateChange();
  1690. // 自动播放下一首(如果有的话)
  1691. const currentPlayMode = this.stateModel.getState().playMode;
  1692. if (this.playlistModel.hasNext(currentPlayMode)) {
  1693. setTimeout(() => {
  1694. this.playNext();
  1695. }, 500);
  1696. }
  1697. }
  1698. onPlaybackError(what: number, extra: number): void {
  1699. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Playback error - what: ${what}, extra: ${extra}`);
  1700. this.stateModel.updateLoadingState(false);
  1701. this.stateModel.updatePlayingState(false);
  1702. this.stopProgressTimer();
  1703. // 保存播放状态变化
  1704. this.saveCurrentState();
  1705. // 创建播放器错误对象
  1706. const playbackError = new PlayerError(
  1707. PlayerErrorType.PLAYBACK_ERROR,
  1708. `Playback error: what=${what}, extra=${extra}`,
  1709. what
  1710. );
  1711. playbackError.recoverable = what !== -1004 && what !== -1007; // 某些错误码可恢复
  1712. playbackError.retryCount = 0;
  1713. // 创建错误上下文
  1714. const errorContext: ErrorContext = {
  1715. currentSong: this.getCurrentSong(),
  1716. playlist: this.getPlaylist(),
  1717. currentIndex: this.getCurrentIndex(),
  1718. retryCount: 0,
  1719. isNetworkAvailable: true // 这里可以实际检查网络状态
  1720. };
  1721. // 使用错误恢复策略处理错误
  1722. this.errorRecovery.handleError(playbackError, errorContext).then((action: ErrorRecoveryAction) => {
  1723. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error recovery action: ${action}`);
  1724. // 这里可以根据 action 执行相应的恢复操作
  1725. }).catch((recoveryError: Error) => {
  1726. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error recovery failed: ${recoveryError}`);
  1727. });
  1728. }
  1729. // ========== 卡片更新相关方法 ==========
  1730. /**
  1731. * 创建当前的卡片数据
  1732. */
  1733. private createWidgetData(): WidgetData {
  1734. const currentState = this.stateModel.getState();
  1735. const currentSong = this.playlistModel.getCurrentSong();
  1736. const currentIndex = this.playlistModel.getCurrentIndex();
  1737. const totalCount = this.playlistModel.getTotalCount();
  1738. const playMode = currentState.playMode;
  1739. return {
  1740. playState: {
  1741. isPlaying: currentState.isPlaying,
  1742. isPaused: currentState.isPaused,
  1743. isLoading: currentState.isLoading
  1744. },
  1745. currentSong: {
  1746. id: currentSong?.id || '',
  1747. title: currentSong?.name || '暂无播放',
  1748. artist: currentSong?.artist || '未知艺术家',
  1749. album: currentSong?.album || '未知专辑',
  1750. coverImagePath: currentSong?.pixelMapPath || '',
  1751. duration: this.parseDuration(currentSong?.duration || '0')
  1752. },
  1753. progress: {
  1754. currentPosition: currentState.currentPosition,
  1755. duration: currentState.duration,
  1756. percentage: currentState.duration > 0 ? (currentState.currentPosition / currentState.duration) * 100 : 0,
  1757. currentTimeText: this.formatTime(currentState.currentPosition),
  1758. totalTimeText: this.formatTime(currentState.duration)
  1759. },
  1760. playlist: {
  1761. hasNext: this.playlistModel.hasNext(playMode),
  1762. hasPrevious: this.playlistModel.hasPrevious(playMode),
  1763. currentIndex: currentIndex,
  1764. totalCount: totalCount
  1765. },
  1766. config: {
  1767. size: WidgetSize.MEDIUM, // 默认尺寸,实际会根据卡片尺寸适配
  1768. theme: WidgetTheme.AUTO,
  1769. showProgress: true,
  1770. showCover: true
  1771. }
  1772. };
  1773. }
  1774. /**
  1775. * 播放状态变化时更新卡片
  1776. */
  1777. private async updateWidgetsForPlayStateChange(): Promise<void> {
  1778. try {
  1779. const widgetData = this.createWidgetData();
  1780. await this.widgetUpdateService.updateAllForms(widgetData);
  1781. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widgets updated for play state change`);
  1782. } catch (error) {
  1783. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update widgets for play state change: ${error}`);
  1784. }
  1785. }
  1786. /**
  1787. * 歌曲变化时更新卡片
  1788. */
  1789. private async updateWidgetsForSongChange(song: VideoItem, forceUpdate: boolean = false): Promise<void> {
  1790. try {
  1791. const widgetData = this.createWidgetData();
  1792. if (forceUpdate) {
  1793. // 强制更新(忽略防抖),用于重要事件如歌曲切换
  1794. await this.widgetUpdateService.forceUpdateAllForms(widgetData);
  1795. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widgets force updated for song change: ${song.name}`);
  1796. } else {
  1797. await this.widgetUpdateService.updateAllForms(widgetData);
  1798. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widgets updated for song change: ${song.name}`);
  1799. }
  1800. } catch (error) {
  1801. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update widgets for song change: ${error}`);
  1802. }
  1803. }
  1804. /**
  1805. * 进度变化时更新卡片(防抖处理)
  1806. */
  1807. private async updateWidgetsForProgressChange(progress: PlayProgress): Promise<void> {
  1808. try {
  1809. // 进度更新很频繁,让防抖机制发挥作用
  1810. const widgetData = this.createWidgetData();
  1811. await this.widgetUpdateService.updateAllForms(widgetData);
  1812. // 不记录日志,避免过多输出
  1813. } catch (error) {
  1814. // 进度更新失败不影响播放,只记录错误
  1815. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update widgets for progress change: ${error}`);
  1816. }
  1817. }
  1818. /**
  1819. * 状态变化时更新卡片
  1820. */
  1821. public async updateWidgetsForStateChange(): Promise<void> {
  1822. try {
  1823. const widgetData = this.createWidgetData();
  1824. await this.widgetUpdateService.updateAllForms(widgetData);
  1825. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widgets updated for state change`);
  1826. } catch (error) {
  1827. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update widgets for state change: ${error}`);
  1828. }
  1829. }
  1830. /**
  1831. * 播放列表变化时更新卡片
  1832. */
  1833. private async updateWidgetsForPlaylistChange(): Promise<void> {
  1834. try {
  1835. const widgetData = this.createWidgetData();
  1836. await this.widgetUpdateService.updateAllForms(widgetData);
  1837. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Widgets updated for playlist change`);
  1838. } catch (error) {
  1839. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update widgets for playlist change: ${error}`);
  1840. }
  1841. }
  1842. /**
  1843. * 应用启动时初始化卡片显示
  1844. */
  1845. public async initializeWidgetDisplay(): Promise<void> {
  1846. try {
  1847. const widgetData = this.createWidgetData();
  1848. await this.widgetUpdateService.forceUpdateAllForms(widgetData);
  1849. LogUtils.getInstance().LOGI('UnifiedPlayerService: Widget display initialized');
  1850. } catch (error) {
  1851. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to initialize widget display: ${error}`);
  1852. }
  1853. }
  1854. /**
  1855. * 获取卡片更新统计信息
  1856. */
  1857. public getWidgetUpdateStats(): UpdateStats {
  1858. return this.widgetUpdateService.getUpdateStats();
  1859. }
  1860. /**
  1861. * 重置卡片更新统计
  1862. */
  1863. public resetWidgetUpdateStats(): void {
  1864. this.widgetUpdateService.resetStats();
  1865. }
  1866. /**
  1867. * 格式化时间为 MM:SS 格式
  1868. */
  1869. private formatTime(timeInMs: number): string {
  1870. const totalSeconds = Math.floor(timeInMs / 1000);
  1871. const minutes = Math.floor(totalSeconds / 60);
  1872. const seconds = totalSeconds % 60;
  1873. return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  1874. }
  1875. /**
  1876. * 解析时长字符串为毫秒数
  1877. */
  1878. private parseDuration(durationStr: string): number {
  1879. if (!durationStr || durationStr === '0') {
  1880. return 0;
  1881. }
  1882. try {
  1883. // 如果是纯数字,假设是秒数
  1884. const numValue = parseFloat(durationStr);
  1885. if (!isNaN(numValue)) {
  1886. const result = Math.floor(numValue * 1000); // 转换为毫秒
  1887. return result;
  1888. }
  1889. // 如果包含冒号,解析为 MM:SS 或 HH:MM:SS 格式
  1890. if (durationStr.includes(':')) {
  1891. const parts = durationStr.split(':').map(part => parseInt(part, 10));
  1892. let totalSeconds = 0;
  1893. if (parts.length === 2) {
  1894. // MM:SS 格式
  1895. totalSeconds = parts[0] * 60 + parts[1];
  1896. } else if (parts.length === 3) {
  1897. // HH:MM:SS 格式
  1898. totalSeconds = parts[0] * 3600 + parts[1] * 60 + parts[2];
  1899. }
  1900. const result = totalSeconds * 1000; // 转换为毫秒
  1901. return result;
  1902. }
  1903. return 0;
  1904. } catch (error) {
  1905. return 0;
  1906. }
  1907. }
  1908. // ==================== PlayerStateCallback 实现 ====================
  1909. /**
  1910. * 转换播放模式为AVSession循环模式
  1911. */
  1912. private convertPlayModeToLoopMode(playMode: number): avSession.LoopMode {
  1913. switch (playMode) {
  1914. case PlayMode.SINGLE_REPEAT:
  1915. return avSession.LoopMode.LOOP_MODE_SINGLE;
  1916. case PlayMode.NORMAL:
  1917. return avSession.LoopMode.LOOP_MODE_LIST;
  1918. case PlayMode.RANDOM:
  1919. return avSession.LoopMode.LOOP_MODE_SHUFFLE;
  1920. case PlayMode.SEQUENCE:
  1921. default:
  1922. return avSession.LoopMode.LOOP_MODE_SEQUENCE;
  1923. }
  1924. }
  1925. /**
  1926. * 更新AVSession播放状态(带防抖)
  1927. */
  1928. private updateSessionPlayState(): void {
  1929. // 清除之前的定时器
  1930. if (this.avSessionUpdateTimer !== -1) {
  1931. clearTimeout(this.avSessionUpdateTimer);
  1932. }
  1933. // 增加防抖延迟到500ms,减少频繁更新
  1934. this.avSessionUpdateTimer = setTimeout(() => {
  1935. this.doUpdateSessionPlayState();
  1936. }, 500);
  1937. }
  1938. /**
  1939. * 实际执行AVSession播放状态更新
  1940. */
  1941. private doUpdateSessionPlayState(): void {
  1942. try {
  1943. if (!this.avSessionController) {
  1944. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller not initialized');
  1945. return;
  1946. }
  1947. // 避免过于频繁的更新(最小间隔500ms)
  1948. const now = Date.now();
  1949. if (now - this.lastAvSessionUpdate < 500) {
  1950. LogUtils.getInstance().LOGI('UnifiedPlayerService: Skipping AVSession update due to rate limit');
  1951. return;
  1952. }
  1953. const currentSong = this.playlistModel.getCurrentSong();
  1954. const currentPosition = this.getCurrentPosition();
  1955. // 优先从播放器获取实际duration
  1956. let duration = 0;
  1957. const ijkPlayer = this.playerManager.getIjkPlayer();
  1958. if (ijkPlayer) {
  1959. duration = ijkPlayer.getDuration();
  1960. }
  1961. if (duration <= 0 && currentSong?.duration) {
  1962. duration = this.parseDuration(currentSong.duration);
  1963. }
  1964. const currentState = this.stateModel.getState();
  1965. // 按照官方文档要求设置完整的播放状态
  1966. const playbackState: avSession.AVPlaybackState = {
  1967. // 播放状态
  1968. state: ijkPlayer?.isPlaying() ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
  1969. // 播放速度
  1970. speed: currentState.speed || 1.0,
  1971. // 循环模式
  1972. loopMode: this.convertPlayModeToLoopMode(currentState.playMode),
  1973. // 收藏状态
  1974. isFavorite: false // 可以根据实际收藏状态设置
  1975. };
  1976. // 只有当duration > 0时才设置位置信息,避免进度条显示问题
  1977. if (duration > 0) {
  1978. playbackState.position = {
  1979. elapsedTime: Math.max(0, currentPosition), // 已播放时间(毫秒)
  1980. updateTime: Date.now() // 更新时间戳
  1981. };
  1982. playbackState.bufferedTime = Math.max(currentPosition, 0);
  1983. } else {
  1984. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Skipping position info due to invalid duration: ${duration}ms`);
  1985. }
  1986. this.avSessionController.setAvSessionPlayState(playbackState);
  1987. // 记录上次更新时间
  1988. this.lastAvSessionUpdate = now;
  1989. LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession play state updated successfully`);
  1990. } catch (error) {
  1991. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession play state: ${error}`);
  1992. }
  1993. }
  1994. /**
  1995. * 设置当前播放模式到AVSession
  1996. */
  1997. private setCurrentPlayMode(): void {
  1998. try {
  1999. if (!this.avSessionController) {
  2000. return;
  2001. }
  2002. const currentSong = this.playlistModel.getCurrentSong();
  2003. if (!currentSong) {
  2004. return;
  2005. }
  2006. // 修复duration转换问题 - 优先从播放器获取实际duration
  2007. let duration = 0;
  2008. try {
  2009. const ijkPlayer = this.playerManager.getIjkPlayer();
  2010. if (ijkPlayer) {
  2011. const playerDuration = ijkPlayer.getDuration();
  2012. if (playerDuration > 0) {
  2013. duration = playerDuration;
  2014. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using player duration: ${duration}ms`);
  2015. } else {
  2016. // 备用方案:解析VideoItem中的duration
  2017. duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
  2018. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using parsed duration: ${duration}ms from '${currentSong.duration}'`);
  2019. }
  2020. } else {
  2021. duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
  2022. LogUtils.getInstance().LOGI(`UnifiedPlayerService: No player available, using parsed duration: ${duration}ms`);
  2023. }
  2024. } catch (error) {
  2025. duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
  2026. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error getting player duration, using parsed: ${duration}ms, error: ${error}`);
  2027. }
  2028. // 获取歌词内容(如果有的话)
  2029. const lyricContent = ''; // 这里可以根据需要获取歌词内容
  2030. this.avSessionController.setAVMetadataMusic(currentSong, duration, lyricContent);
  2031. LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession metadata updated for ${currentSong.name} with duration ${duration}ms`);
  2032. } catch (error) {
  2033. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set current play mode: ${error}`);
  2034. }
  2035. }
  2036. /**
  2037. * 更新AVSession元数据(带防抖和智能更新)
  2038. */
  2039. private async updateAvSessionMetadata(song: VideoItem): Promise<void> {
  2040. // 清除之前的定时器
  2041. if (this.avMetadataUpdateTimer !== -1) {
  2042. clearTimeout(this.avMetadataUpdateTimer);
  2043. }
  2044. // 如果播放器还没准备好,延迟更新等待播放器准备完成
  2045. const ijkPlayer = this.playerManager.getIjkPlayer();
  2046. const playerDuration = ijkPlayer ? ijkPlayer.getDuration() : 0;
  2047. const shouldWaitForPlayer = playerDuration <= 0 && this.isPlayerPreparedForCurrentSong === false;
  2048. if (shouldWaitForPlayer) {
  2049. // 播放器还没准备好,延迟更新
  2050. this.avMetadataUpdateTimer = setTimeout(() => {
  2051. this.doUpdateAvSessionMetadata(song);
  2052. }, 800);
  2053. } else {
  2054. // 播放器已准备好或者不需要等待,稍微延迟更新
  2055. this.avMetadataUpdateTimer = setTimeout(() => {
  2056. this.doUpdateAvSessionMetadata(song);
  2057. }, 300);
  2058. }
  2059. }
  2060. /**
  2061. * 实际执行AVSession元数据更新
  2062. */
  2063. private async doUpdateAvSessionMetadata(song: VideoItem): Promise<void> {
  2064. try {
  2065. if (!this.avSessionController) {
  2066. LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller not initialized');
  2067. return;
  2068. }
  2069. // 避免过于频繁的元数据更新(最小间隔600ms)
  2070. const now = Date.now();
  2071. if (now - this.lastAvMetadataUpdate < 600) {
  2072. LogUtils.getInstance().LOGI('UnifiedPlayerService: Skipping AVSession metadata update due to rate limit');
  2073. return;
  2074. }
  2075. // 调试:打印VideoItem的duration字段
  2076. LogUtils.getInstance().LOGI(`UnifiedPlayerService: VideoItem duration field - value: '${song.duration}', type: ${typeof song.duration}`);
  2077. // 智能获取最准确的duration
  2078. let duration = 0;
  2079. try {
  2080. const ijkPlayer = this.playerManager.getIjkPlayer();
  2081. if (ijkPlayer && this.isPlayerPreparedForCurrentSong) {
  2082. const playerDuration = ijkPlayer.getDuration();
  2083. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player getDuration() returned: ${playerDuration}ms`);
  2084. if (playerDuration > 0) {
  2085. duration = playerDuration;
  2086. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using player duration: ${duration}ms`);
  2087. } else {
  2088. // 备用方案:解析VideoItem中的duration
  2089. duration = song.duration ? this.parseDuration(song.duration) : 0;
  2090. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player duration invalid, using parsed duration: ${duration}ms from '${song.duration}'`);
  2091. }
  2092. } else {
  2093. duration = song.duration ? this.parseDuration(song.duration) : 0;
  2094. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player not ready, using parsed duration: ${duration}ms from '${song.duration}'`);
  2095. }
  2096. } catch (error) {
  2097. duration = song.duration ? this.parseDuration(song.duration) : 0;
  2098. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error getting player duration, using parsed: ${duration}ms from '${song.duration}', error: ${error}`);
  2099. }
  2100. // 最终验证duration
  2101. if (isNaN(duration) || duration <= 0) {
  2102. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Final duration is invalid (${duration}), setting to 0`);
  2103. duration = 0;
  2104. }
  2105. // 获取歌词内容(如果有的话)
  2106. const lyricContent = song.lyricContent || '';
  2107. LogUtils.getInstance().LOGI(`UnifiedPlayerService: About to call setAVMetadataMusic with duration: ${duration}ms (type: ${typeof duration})`);
  2108. await this.avSessionController.setAVMetadataMusic(song, duration, lyricContent);
  2109. // 记录更新时间
  2110. this.lastAvMetadataUpdate = now;
  2111. LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession metadata updated for ${song.name} with duration ${duration}ms`);
  2112. } catch (error) {
  2113. LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession metadata: ${error}`);
  2114. }
  2115. }
  2116. // ==================== 辅助方法 ====================
  2117. }