UnifiedPlayerService.ets 104 KB

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