UnifiedPlayerService.ets 94 KB

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