UnifiedPlayerService.ets 90 KB

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