| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451 |
- import { VideoItem } from '../../viewmodel/VideoItem';
- import { PlayerManager, IPlayerManager, PlayerStateCallback } from './PlayerManager';
- import { MD5 } from '@pura/harmony-utils';
- import {
- PlayerStateModel,
- PlayerState,
- PlayerStateListener,
- PlayMode,
- PlayerError,
- PlayerErrorType
- } from './PlayerStateModel';
- import { PlaylistModel } from './PlaylistModel';
- import { IjkMediaPlayer, LogUtils, InterruptEvent, InterruptHintType } from '@ohos/ijkplayer';
- import { common, Context } from '@kit.AbilityKit';
- import { PreferencesUtil as PuraPreferencesUtil, StrUtil } from '@pura/harmony-utils';
- import {
- DataPersistenceService,
- PlaylistSyncService,
- PlaylistSyncListener,
- IDataPersistenceService,
- PlaylistData,
- PlayerStateData
- } from './DataPersistenceService';
- import {
- ErrorRecoveryStrategy,
- IErrorRecoveryStrategy,
- ErrorContext,
- ErrorRecoveryAction
- } from './ErrorRecoveryStrategy';
- import { AvSessionController } from '../../controller/AvSessionController';
- import { avSession } from '@kit.AVSessionKit';
- import { formProvider, formBindingData } from '@kit.FormKit';
- import { fileIo } from '@kit.CoreFileKit';
- import { http } from '@kit.NetworkKit';
- import { util } from '@kit.ArkTS';
- import json from '@ohos.util.json';
- import MediaTable from '../util/MediaTable';
- import { Utility } from '../util/Utility';
- import { FormRdbHelper } from '../../database/FormRdbHelper';
- import { FormInfo } from '../../viewmodel/FormInfo';
- import { WidgetDataRdbHelper } from '../../database/WidgetDataRdbHelper';
- import { WidgetDataInfo } from '../../viewmodel/WidgetDataInfo';
- import { PreferencesUtil, IWidgetData } from '../utils/PreferencesUtil';
- import { SettingPage } from '../../pages/SettingPage';
- /**
- * 跳过设置接口
- */
- interface JumpSettings {
- isEnabled: boolean;
- jumpTopTime: number;
- jumpEndTime: number;
- }
- /**
- * 存储方式枚举
- */
- enum WidgetDataStorageType {
- DATABASE = 'database', // 使用数据库存储
- PREFERENCES = 'preferences' // 使用Preferences存储
- }
- /**
- * 控制WidgetData存储方式的配置
- * 可以通过修改这个值来切换存储方式
- */
- const WIDGET_DATA_STORAGE_TYPE: WidgetDataStorageType = WidgetDataStorageType.DATABASE;
- /**
- * 服务就绪状态详情接口
- */
- export interface ServiceReadinessDetails {
- initialized: boolean;
- hasPlayerInstance: boolean;
- hasContext: boolean;
- dataRestored: boolean;
- playlistSize: number;
- currentIndex: number;
- }
- /**
- * 服务就绪信息接口
- */
- export interface ServiceReadinessInfo {
- isReady: boolean;
- details: ServiceReadinessDetails;
- }
- /**
- * 初始化验证结果接口
- */
- export interface InitializationValidation {
- isValid: boolean;
- errors: string[];
- warnings: string[];
- }
- /**
- * 卡片播放状态数据接口
- */
- export interface WidgetPlayerState {
- isPlaying: boolean;
- isPaused: boolean;
- isLoading: boolean;
- currentPosition: number;
- duration: number;
- hasNext: boolean;
- hasPrevious: boolean;
- playMode: number;
- }
- /**
- * 卡片播放列表信息接口
- */
- export interface WidgetPlaylistInfo {
- currentIndex: number;
- totalCount: number;
- }
- /**
- * 卡片时间信息接口
- */
- export interface WidgetTimeInfo {
- currentTimeText: string;
- totalTimeText: string;
- progressPercentage: number;
- }
- /**
- * 缓存文件信息接口
- */
- export interface CacheFileInfo {
- path: string;
- size: number;
- mtime: number;
- }
- /**
- * 卡片数据接口
- */
- export interface WidgetFormData {
- // VideoItem数据
- id: string;
- name: string;
- artist: string;
- album: string;
- pixelMapPath: string;
- duration: number;
- filePath: string;
- // 扩展数据
- imgName: string;
- imageColorHex: string;
- // 状态数据
- playerState: WidgetPlayerState;
- playlistInfo: WidgetPlaylistInfo;
- timeInfo: WidgetTimeInfo;
- // 卡片图片显示必填字段
- formImages: Record<string, number>;
- }
- /**
- * 缓存的静态卡片数据接口
- * 用于缓存不会随播放状态改变的数据,避免重复计算和IO
- */
- interface CachedWidgetData {
- // 歌曲的基本信息
- id: string;
- name: string;
- artist: string;
- album: string;
- pixelMapPath: string; // 原始封面路径
- duration: number;
- filePath: string;
- imageColorHex: string;
- // 处理后的最终图片路径(本地或缓存),用于快速打开
- finalImagePath: string | null;
- }
- interface ImageCacheInfo {
- path: string;
- imgName: string;
- }
- /**
- * 统一播放器服务接口
- * 提供统一的播放控制接口,替代LocalMusic中的播放器逻辑
- */
- export interface IPlayerService {
- /**
- * 启动播放或恢复播放
- * @returns
- */
- startPlayOrResumePlay(): Promise<void>;
- /**
- * 暂停播放
- * @returns
- */
- pause(): Promise<void>;
- /**
- * 停止播放
- * @returns
- */
- stop(): Promise<void>;
- /**
- * 跳转到指定位置
- * @param position
- * @returns
- */
- seekTo(position: string): Promise<void>;
- // 播放列表控制 - 基于LocalMusic现有方法
- /**
- * 播放下一首
- * @returns
- */
- playNext(): Promise<void>;
- /**
- * 播放上一首
- */
- playPrevious(): Promise<void>;
- /**
- * 播放指定索引的歌
- * @param index
- * @returns
- */
- playSongAtIndex(index: number): Promise<void>;
- /**
- * 获取播放器状态
- * @returns
- */
- getCurrentState(): PlayerState;
- /**
- * 获取实际播放状态
- * @returns 实际播放状态
- */
- getActualPlayingState(): boolean;
- /**
- * 获取当前播放歌曲
- * @returns
- */
- getCurrentSong(): VideoItem | null;
- /**
- * 获取播放列表
- * @returns
- */
- getPlaylist(): VideoItem[];
- /**
- * 获取当前播放索引
- * @returns
- */
- getCurrentIndex(): number;
- /**
- * 获取当前播放进度
- * @returns 当前播放进度
- */
- getCurrentPosition(): number;
- /**
- * 获取播放器实例
- * @returns 播放器实例
- */
- getIjkPlayer(): IjkMediaPlayer | null;
- /**
- * 是否数据恢复完成
- * @returns 是否数据恢复完成
- * */
- isDataRestorationCompleted(): boolean;
- /**
- * 等待数据恢复完成
- * @param maxWaitMs 最大等待时间(毫秒),默认为-1(无限等待)
- * @returns 是否恢复完成
- */
- waitForDataRestoration(maxWaitMs?: number): Promise<boolean>;
- /**
- * 强制数据恢复
- * @returns 是否恢复成功
- */
- forceDataRestoration(): Promise<boolean>;
- /**
- * 是否就绪
- * @returns
- */
- isAllServicesReady(): boolean;
- /**
- * 处理音频中断事件
- * @param event 音频中断事件
- */
- handleAudioInterrupt(event: InterruptEvent): Promise<void>;
- /**
- * 等待服务就绪
- * @param maxWaitMs 最大等待时间(毫秒),默认为-1(无限等待)
- * @param checkDataRestore 是否检查数据恢复状态,默认为true
- * @returns 是否就绪
- */
- waitForAllServicesReady(maxWaitMs?: number, checkDataRestore?: boolean): Promise<boolean>;
- /**
- * 获取服务就绪信息
- * @returns 服务就绪信息
- */
- getServiceReadinessInfo(): ServiceReadinessInfo;
- /**
- * 初始化验证
- * @returns 初始化验证结果
- */
- validateInitialization(): InitializationValidation;
- /**
- * 设置播放模式
- * @param mode
- */
- setPlayMode(mode: number): void;
- /**
- * 获取播放模式
- * @returns 播放模式
- */
- getPlayMode(): number;
- /**
- * 设置音量
- * @param volume
- */
- setVolume(volume: number): void;
- /**
- * 设置播放速度
- * @param speed
- */
- setPlaybackSpeed(speed: number): void;
- // 播放列表管理
- /**
- * 设置播放列表
- * @param songs
- * @param currentIndex
- */
- setPlaylist(songs: VideoItem[], currentIndex?: number): void;
- /**
- * 添加歌曲到播放列表
- * @param song
- * @param index
- */
- addToPlaylist(song: VideoItem, index?: number): void;
- /**
- * 移除播放列表中的歌曲
- * @param index
- * @returns 移除的歌曲
- */
- removeFromPlaylist(index: number): VideoItem | null;
- // 事件监听
- /**
- * 添加播放器状态监听
- * @param listener
- */
- addStateListener(listener: PlayerStateListener): void;
- /**
- * 移除播放器状态监听
- * @param listener
- */
- removeStateListener(listener: PlayerStateListener): void;
- /**
- * 初始化
- * @param context
- * @returns
- */
- initialize(context: common.UIAbilityContext): Promise<void>;
- /**
- * 释放资源
- */
- release(): void;
- /**
- * 更新收藏列表
- */
- updateFavoriteList(): Promise<void>;
- /**
- * 强制刷新收藏状态(用于收藏/取消收藏操作后)
- */
- refreshFavoriteStatus(): Promise<void>;
- /**
- * 获取收藏列表
- * @returns 收藏列表
- */
- getFav(): Array<VideoItem>;
- syncPlaylistToService(songList: VideoItem[], currentIndex: number, playType: PlayMode): void
- }
- /**
- * 统一播放器服务实现
- * 整合PlayerManager、PlayerStateModel和PlaylistModel
- */
- interface widgeData {
- // VideoItem数据平铺
- id: string;
- name: string;
- artist: string;
- album: string;
- pixelMapPath: string;
- duration: number;
- filePath: string;
- imgName: string;
- imageColorHex: string;
- isFavorite: boolean;
- // PlayerState数据平铺
- isPlaying: boolean;
- isPaused: boolean;
- isLoading: boolean;
- currentPosition: number;
- hasNext: boolean;
- hasPrevious: boolean;
- playMode: number;
- // 播放列表信息平铺
- currentIndex: number;
- totalCount: number;
- // 时间信息平铺
- currentTimeText: string;
- totalTimeText: string;
- progressPercentage: number;
- // 保持嵌套结构兼容性
- playerState: WidgetPlayerState;
- playlistInfo: WidgetPlaylistInfo;
- timeInfo: WidgetTimeInfo;
- // 卡片图片显示必填字段
- formImages: Record<string, number>;
- }
- interface PlayPauseInterface {
- widgetIsPlaying?: boolean;
- }
- export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, PlayerStateCallback {
- private static instance: UnifiedPlayerService | null = null;
- private playerManager: IPlayerManager;
- private stateModel: PlayerStateModel;
- private playlistModel: PlaylistModel;
- private dataPersistence: IDataPersistenceService;
- private playlistSync: PlaylistSyncService;
- private errorRecovery: IErrorRecoveryStrategy;
- private avSessionController: AvSessionController | null = null;
- private context: common.UIAbilityContext | null = null;
- // 防重复调用机制
- private isUpdatingForms: boolean = false;
- private lastAvSessionUpdateTime: number = 0;
- private progressTimer: number = -1;
- private initialized: boolean = false;
- private isDataRestored: boolean = false; // 新增:数据恢复完成标识
- private isPlayerPreparedForCurrentSong: boolean = false; // 新增:播放器是否已为当前歌曲准备就绪
- private currentRetryCount: number = 0;
- private lastAvSessionUpdate: number = 0;
- private lastAvMetadataUpdate: number = 0; // 新增:上次元数据更新时间
- private avSessionUpdateTimer: number = -1; // 新增:系统播控更新防抖定时器
- private avMetadataUpdateTimer: number = -1; // 新增:元数据更新防抖定时器
- private favList: VideoItem[] = [];
- private table: MediaTable | undefined = undefined;
- private lastAutoPlayTime: number = 0; // 新增:上次自动播放时间,用于防抖
- private autoPlayDebounceMs: number = 1000; // 自动播放防抖间隔(毫秒)
- private isPausingInProgress: boolean = false; // 新增:标记是否正在执行暂停操作
- private isManualSongChange: boolean = false; // 新增:是否正在进行手动歌曲切换
- // 图片缓存相关属性
- private imageCacheDir: string = ''; // 图片缓存目录
- private lastImageCache: ImageCacheInfo | null = null; // 图片缓存优化
- private lastFormUpdateTime: number = 0; // 上次卡片更新时间,用于防抖
- private readonly CACHE_EXPIRY_DAYS: number = 7; // 缓存有效期:7天
- private readonly MAX_CACHE_SIZE_MB: number = 50; // 最大缓存大小:50MB
- private hasChecked: boolean=false;
- // 卡片数据缓存
- private widgetDataCache: CachedWidgetData | null = null;
- private constructor() {
- this.playerManager = PlayerManager.getInstance();
- this.stateModel = new PlayerStateModel();
- this.playlistModel = new PlaylistModel();
- this.dataPersistence = DataPersistenceService.getInstance();
- this.playlistSync = PlaylistSyncService.getInstance();
- this.errorRecovery = ErrorRecoveryStrategy.getInstance();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Instance created');
- }
- public static getInstance(): UnifiedPlayerService {
- if (!UnifiedPlayerService.instance) {
- UnifiedPlayerService.instance = new UnifiedPlayerService();
- }
- return UnifiedPlayerService.instance;
- }
- async initialize(context: common.UIAbilityContext): Promise<void> {
- if (this.initialized && this.context === context) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService已经初始化过了');
- return;
- }
- try {
- console.log("Heanup2 UnifiedPlayerService: 开始快速初始化服务");
- this.context = context;
- // 初始化图片缓存目录
- await this.initImageCacheDirectory();
- // 快速初始化核心组件
- this.playerManager.setStateCallback(this);
- // 初始化PlaylistModel的MediaTable
- this.playlistModel.initializeMediaTable(context);
- // 设置音频中断监听
- // 音频中断处理已移至LocalMusic中,响应更快
- // this.setupAudioInterruptListener();
- // 设置同步监听器
- this.playlistSync.addSyncListener(this);
- // 设置状态模型监听器,自动广播状态变化
- this.setupProgressTimer();
- this.initialized = true;
- // 异步初始化耗时组件,避免阻塞
- this.initializeHeavyComponentsAsync(context);
- // 清理无效的Form ID
- this.cleanupInvalidFormIds();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Core initialization completed');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService initialization error: ${error}`);
- throw new Error(`初始化失败: ${error}`);
- }
- }
- /**
- * 为卡片进程设置context
- * 卡片进程是独立的,需要单独设置context
- */
- setFormContext(context: common.UIAbilityContext | common.FormExtensionContext): void {
- try {
- this.context = context as common.UIAbilityContext;
- // 同时设置到AppStorage,确保卡片进程中其他地方能访问
- AppStorage.setOrCreate('context', context);
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Form context set successfully');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to set form context: ${error}`);
- }
- }
- /**
- * 获取当前context
- */
- getContext(): common.UIAbilityContext | null {
- // 优先返回实例的context
- if (this.context) {
- return this.context;
- }
-
- // 尝试从AppStorage获取
- try {
- const appStorageContext = AppStorage.get('context') as common.UIAbilityContext;
- if (appStorageContext) {
- this.context = appStorageContext;
- return appStorageContext;
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to get context from AppStorage: ${error}`);
- }
-
- return null;
- }
- /**
- * 异步初始化耗时组件
- */
- private initializeHeavyComponentsAsync(context: common.UIAbilityContext): void {
- setTimeout(async () => {
- try {
- console.log("Heanup2 UnifiedPlayerService: 开始异步初始化耗时组件");
- // 并行初始化各个服务以提高效率
- const initPromises = [
- this.playerManager.initialize(context),
- this.dataPersistence.initialize(context),
- this.playlistSync.initialize(context),
- ];
- await Promise.all(initPromises);
- // 立即尝试快速恢复关键数据(从AppStorage)
- await this.fastRestoreFromMemory();
- this.initializeAvSession();
- // 并行初始化收藏列表和恢复数据,提高效率
- const initPromises2 = [
- this.restorePersistedState(),
- this.initializeFavoriteList()
- ];
- await Promise.all(initPromises2);
- // 数据恢复完成后,同步播放器状态到UI
- setTimeout(async () => {
- try {
- this.saveCurrentState()
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Player state synced to UI after initialization');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to sync player state after initialization: ${error}`);
- }
- }, 500); // 延迟500ms确保初始化完全完成
- console.log("Heanup2 UnifiedPlayerService: 异步组件初始化完成");
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Heavy components initialized');
- } catch (error) {
- console.log(`Heanup2 UnifiedPlayerService: 异步组件初始化失败: ${error}`);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService heavy components init error: ${error}`);
- // 即使失败也设置数据恢复完成,避免无限等待
- this.isDataRestored = true;
- }
- }, 10); // 缩短延迟,让数据恢复更快开始
- }
- // 播放控制方法
- async startPlayOrResumePlay(): Promise<void> {
- try {
- console.log("Heanup2 UnifiedPlayerService: 开始恢复播放");
- // 优化的服务就绪检查 - 支持冷启动场景
- if (!this.isAllServicesReady()) {
- console.log("Heanup2 UnifiedPlayerService: 服务未就绪,等待服务初始化完成");
- const isReady = await this.waitForAllServicesReady(5000, true); // 增加等待时间并启用数据恢复检查
- if (!isReady) {
- console.log("Heanup2 UnifiedPlayerService: 服务等待超时,尝试强制数据恢复...");
- // 尝试强制数据恢复作为最后手段
- const forceRestored = await this.forceDataRestoration();
- if (!forceRestored) {
- const error = new Error('Services not ready for playback after force restoration');
- await this.handlePlaybackError(error, PlayerErrorType.INITIALIZATION_ERROR);
- return;
- }
- console.log("Heanup2 UnifiedPlayerService: 强制数据恢复成功,继续播放流程");
- }
- }
- // 关键修复:在执行任何操作之前,首先检查播放器的实际状态。
- // 如果播放器已经在播放,只需同步状态并立即返回,避免不必要的重置。
- const ijkPlayer = this.playerManager.getIjkPlayer()!; // 服务就绪后播放器一定存在
- if (ijkPlayer.isPlaying()) {
- console.log("Heanup2 UnifiedPlayerService: 检测到播放器已在播放。同步状态并退出。");
- this.syncPlayingState();
- return;
- }
- // 获取当前歌曲 - 如果没有就尝试恢复
- let currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong) {
- console.log("Heanup2 UnifiedPlayerService: 没有可播放的歌曲,尝试恢复持久化状态");
- await this.restorePersistedState();
- currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong) {
- console.log("Heanup2 UnifiedPlayerService: 恢复后仍然没有可播放的歌曲");
- // 冷启动优化:播放列表为空时的友好处理
- await this.handleEmptyPlaylistScenario();
- return;
- }
- console.log("Heanup2 UnifiedPlayerService: 恢复成功,当前歌曲信息:" + JSON.stringify(currentSong));
- }
- this.stateModel.updateLoadingState(true);
- // 简化的播放器状态检查 - 检查是否可以从暂停恢复
- const shouldResumeFromPause = this.canResumeFromPause(ijkPlayer);
- if (shouldResumeFromPause) {
- console.log("Heanup2 UnifiedPlayerService: 从暂停状态恢复播放");
- return await this.resumeFromPause();
- }
- // 新歌播放前的必要检查
- await this.validateSongForPlayback(currentSong);
- // 设置播放器并开始播放
- await this.setupAndStartPlayback(currentSong);
- await this.doUpdateAvSessionMetadata(currentSong)
- } catch (error) {
- this.stateModel.updateLoadingState(false);
- this.stateModel.updatePlayingState(false);
- await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
- }
- }
- /**
- * 处理空播放列表场景(冷启动优化)
- */
- private async handleEmptyPlaylistScenario(): Promise<void> {
- try {
- console.log("Heanup2 UnifiedPlayerService: 处理空播放列表场景");
-
- // 停止加载状态
- this.stateModel.updateLoadingState(false);
- this.stateModel.updatePlayingState(false);
-
- // 尝试从数据库快速加载一些歌曲(如果有)
- const quickSongs = await this.loadQuickPlaylistFromDatabase();
- if (quickSongs && quickSongs.length > 0) {
- console.log(`Heanup2 UnifiedPlayerService: 从数据库快速加载${quickSongs.length}首歌曲`);
- this.setPlaylist(quickSongs, 0);
-
- // 再次尝试播放
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- await this.validateSongForPlayback(currentSong);
- await this.setupAndStartPlayback(currentSong);
- return;
- }
- }
-
- // 如果仍然没有歌曲,提供友好的用户提示
- console.log("Heanup2 UnifiedPlayerService: 无可播放的歌曲,等待用户添加歌曲");
-
- // 更新widget显示空状态
- await this.updateAllForms();
-
- } catch (error) {
- console.log(`Heanup2 UnifiedPlayerService: 处理空播放列表场景失败: ${error}`);
- }
- }
- /**
- * 从数据库快速加载播放列表(用于冷启动场景)
- */
- private async loadQuickPlaylistFromDatabase(): Promise<VideoItem[] | null> {
- try {
- if (!this.context) {
- return null;
- }
-
- // 尝试从数据库加载最近播放的歌曲
- const table = this.getTable();
- return new Promise<VideoItem[]>((resolve) => {
- table.queryRecentPlayedRecords(10, (songs: VideoItem[]) => {
- if (songs && songs.length > 0) {
- console.log(`Heanup2 UnifiedPlayerService: 从数据库加载到${songs.length}首最近播放的歌曲`);
- resolve(songs);
- } else {
- // 如果没有最近播放,尝试加载任意歌曲
- table.query(0, (allSongs: VideoItem[]) => {
- console.log(`Heanup2 UnifiedPlayerService: 从数据库加载到${allSongs?.length || 0}首所有歌曲`);
- resolve(allSongs || []);
- }, true);
- }
- });
- });
- } catch (error) {
- console.log(`Heanup2 UnifiedPlayerService: 从数据库加载播放列表失败: ${error}`);
- return null;
- }
- }
- /**
- * 检查是否可以从暂停状态恢复播放
- */
- private canResumeFromPause(ijkPlayer: IjkMediaPlayer): boolean {
- const isPausedByManager = this.playerManager.isPausedState();
- const isCurrentlyPlaying = ijkPlayer.isPlaying();
- const playerDuration = ijkPlayer.getDuration();
- const hasValidMedia = playerDuration > 0;
- // 如果认为是暂停但没有有效媒体,清除假暂停状态
- if (isPausedByManager && !hasValidMedia) {
- console.log("Heanup2 UnifiedPlayerService: 检测到假暂停状态,清除暂停标记");
- this.playerManager.clearPausedState();
- this.isPlayerPreparedForCurrentSong = false;
- return false;
- }
- // 如果播放器正在播放,同步状态
- if (isCurrentlyPlaying) {
- this.syncPlayingState();
- return false; // 不需要恢复,已经在播放
- }
- // 检查是否可以从暂停恢复
- const canResume = isPausedByManager &&
- !isCurrentlyPlaying &&
- hasValidMedia &&
- this.isPlayerPreparedForCurrentSong;
- console.log(`Heanup2 UnifiedPlayerService: 暂停恢复检查 - canResume: ${canResume}`);
- return canResume;
- }
- /**
- * 同步播放状态
- */
- private syncPlayingState(): void {
- console.log("Heanup2 UnifiedPlayerService: 播放器正在播放,同步状态");
- this.stateModel.updateLoadingState(false);
- this.stateModel.updatePlayingState(true);
- this.playerManager.clearPausedState();
-
- // 关键修复:确保播放器准备状态标记被正确设置
- this.isPlayerPreparedForCurrentSong = true;
- console.log("Heanup2 UnifiedPlayerService: 设置播放器准备状态为true,确保进度更新正常");
-
- this.startProgressTimer();
- this.updateSessionPlayState();
- this.updateWidgetsForStateChange();
- // 关键修复:使用强制更新确保状态同步时卡片立即更新
- this.updateAllForms(true);
- }
- public async toggleFavorite(): Promise<void> {
- let isFav = 0
- let item = this.getCurrentSong() as VideoItem;
- if (Utility.getIsFav(this.getFav(), item)) {
- isFav = 0
- } else {
- isFav = 1
- }
- return new Promise<void>((resolve) => {
- this.getTable().updateIsFavByFilePath(item.filePath, isFav, async (result: boolean) => {
- if (result) {
- // 刷新UnifiedPlayerService的收藏状态,这会自动更新AVSession
- console.log("Heanup2 UnifiedPlayerService toggleFavorite: 收藏状态切换");
- await this.refreshFavoriteStatus();
- // 立即更新桌面卡片状态,确保收藏图标正确显示
- await this.updateAllForms(true);
- console.log("Heanup2 UnifiedPlayerService toggleFavorite: 收藏状态和卡片状态更新完成");
- }
- resolve();
- })
- });
- }
- /**
- * 从暂停状态恢复播放
- */
- private async resumeFromPause(): Promise<void> {
- try {
- console.log("Heanup2 UnifiedPlayerService: 直接从暂停状态恢复播放");
- this.stateModel.updateLoadingState(false);
- await this.playerManager.startPlayOrResumePlay();
- this.stateModel.updatePlayingState(true);
- this.startProgressTimer();
- setTimeout(() => this.updateProgress(), 100);
- this.saveCurrentState();
- this.updateSessionPlayState();
- await this.updateWidgetsForPlayStateChange();
- // 关键修复:使用强制更新确保卡片状态立即同步
- await this.updateAllForms(true);
- } catch (error) {
- throw new Error(`Resume from pause failed: ${error}`);
- }
- }
- /**
- * 验证歌曲是否可以播放
- */
- private async validateSongForPlayback(song: VideoItem): Promise<void> {
- // 检查文件是否存在(对于本地文件)
- if (!song.filePath.startsWith('http')) {
- const fileExists = await this.checkFileExists(song.filePath);
- if (!fileExists) {
- throw new Error(`File not found: ${song.filePath}`);
- }
- } else {
- // 检查网络连接(对于网络资源)
- const networkAvailable = await this.checkNetworkConnection();
- if (!networkAvailable) {
- throw new Error('Network not available');
- }
- }
- }
- /**
- * 设置播放器并开始播放
- */
- private async setupAndStartPlayback(song: VideoItem): Promise<void> {
- console.log(`Heanup2 UnifiedPlayerService: 设置播放器并开始播放: ${song.name}`);
- // 在随机播放模式下,确保播放历史被正确维护
- const playMode = this.stateModel.getState().playMode;
- if (playMode === PlayMode.RANDOM) {
- await this.playlistModel.ensureCurrentSongInHistory();
- }
- // 设置播放源和配置
- await this.setupPlayerForSong(song);
- // 准备播放器(实际播放会在 onPrepared 回调中开始)
- const preparedPlayer = this.playerManager.getIjkPlayer();
- if (preparedPlayer) {
- preparedPlayer.prepareAsync();
- }
- // 立即更新基础元数据(使用解析的duration),后续在播放器准备好后会用实际duration更新
- await this.updateAvSessionMetadata(song);
-
- // 注释掉此处的记忆播放恢复,统一在onPrepared中处理
- // this.restorePlaybackPosition(song);
-
- this.currentRetryCount = 0;
- }
- /**
- * 直接播放新歌曲,避免恢复逻辑干扰
- * 专为 playNext/playPrevious 设计
- */
- private async playNewSongDirectly(song: VideoItem | null, shouldAutoPlay: boolean = true): Promise<void> {
- if (!song) {
- throw new Error('No song provided for direct play');
- }
- this.stateModel.updateLoadingState(true);
- const playerInstance = this.playerManager.getIjkPlayer();
- if (!playerInstance) {
- throw new Error('Player not initialized');
- }
- // 检查文件是否存在(对于本地文件)
- if (!song.filePath.startsWith('http')) {
- const fileExists = await this.checkFileExists(song.filePath);
- if (!fileExists) {
- throw new Error(`File not found: ${song.filePath}`);
- }
- }
- // 在随机播放模式下,确保播放历史被正确维护
- const playMode = this.stateModel.getState().playMode;
- if (playMode === PlayMode.RANDOM) {
- await this.playlistModel.ensureCurrentSongInHistory();
- }
- // 关键修复:设置播放器的自动播放状态
- this.playerManager.setShouldAutoPlay(shouldAutoPlay);
-
- // 如果需要自动播放,清除暂停状态,确保播放器能正常启动
- if (shouldAutoPlay) {
- this.playerManager.clearPausedState();
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: playNewSongDirectly - cleared paused state for auto-play`);
- }
-
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: playNewSongDirectly - shouldAutoPlay: ${shouldAutoPlay}`);
- // 设置播放源和配置
- await this.setupPlayerForSong(song);
- // 准备播放器
- const preparedPlayer = this.playerManager.getIjkPlayer();
- if (preparedPlayer) {
- preparedPlayer.prepareAsync();
- // 注意:实际播放和状态更新会在 onPrepared 回调中开始
- }
- // 重置重试计数
- this.currentRetryCount = 0;
- }
- async pause(): Promise<void> {
- try {
- // 设置暂停进行中标志,避免状态不匹配误判
- this.isPausingInProgress = true;
- // 保存当前播放位置
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- this.savePlaybackPosition(currentSong);
- }
- // 暂停播放器 - 确保先暂停播放器
- this.playerManager.pausePlayback();
- // 立即更新状态模型为暂停状态,确保状态同步
- this.stateModel.updatePlayingState(false);
- // 停止进度定时器
- this.stopProgressTimer();
- // 等待一小段时间确保播放器状态稳定
- // await new Promise<void>(resolve => setTimeout(resolve, 50));
- // 强制更新AVSession状态,确保播控中心显示正确的暂停状态
- // 注意:在保存状态之前更新AVSession,避免状态不匹配检测的干扰
- this.updateSessionPlayState(true);
- // 保存播放状态变化
- this.saveCurrentState();
- // 更新卡片显示暂停状态
- await this.updateWidgetsForPlayStateChange();
- // 关键修复:使用强制更新确保卡片状态立即同步
- await this.updateAllForms(true);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService pause error: ${error}`);
- } finally {
- // 清除暂停进行中标志
- this.isPausingInProgress = false;
- }
- }
- async stop(): Promise<void> {
- try {
- // 保存当前歌曲的播放位置
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- this.savePlaybackPosition(currentSong);
- }
-
- this.playerManager.stopPlayback();
- // 重要:停止时应该清除所有播放状态,不是暂停状态
- this.stateModel.updatePlayingState(false);
- // this.stateModel.state.isPaused = false; // 停止不是暂停
- this.stateModel.updateProgress(0, 0);
- this.stopProgressTimer();
- // 通知状态变化
- this.stateModel.notifySongChanged(this.getCurrentSong() as VideoItem);
- // 更新卡片显示停止状态
- await this.updateWidgetsForPlayStateChange();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Stop completed, all states cleared');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService stop error: ${error}`);
- throw new Error;
- }
- }
- /**
- * 静默停止播放,不触发状态变化通知(用于歌曲切换)
- */
- private async stopSilently(): Promise<void> {
- try {
- // 静默停止播放,不保存播放位置(位置已经在外部保存)
- this.playerManager.stopPlayback();
- this.stopProgressTimer();
- // 不调用 updatePlayingState,避免触发状态变化通知
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService stopSilentlyWithoutSave error: ${error}`);
- throw new Error;
- }
- }
- private async stopSilentlyWithoutSave(): Promise<void> {
- try {
- // 静默停止播放,不保存播放位置(位置已经在外部保存),也不触发回调
- this.playerManager.stopPlaybackSilently();
- this.stopProgressTimer();
- // 不调用 updatePlayingState,避免触发状态变化通知
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService stopSilentlyWithoutSave error: ${error}`);
- throw new Error;
- }
- }
- async seekTo(position: string): Promise<void> {
- try {
- const currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong) {
- throw new Error('No song to seek');
- }
- // 检查是否支持拖动(wma格式不支持)
- if (StrUtil.isNotEmpty(currentSong.filePath) &&
- currentSong.filePath.toLowerCase().endsWith('.wma')) {
- throw new Error('WMA format does not support seeking');
- }
- await this.playerManager.seekToPosition(position);
- } catch (error) {
- throw new Error(`Failed to seek to position: ${error}`);
- }
- }
- // 播放列表控制方法
- async playNext(): Promise<void> {
- return this.changeTrack('next');
- }
- async playPrevious(): Promise<void> {
- return this.changeTrack('previous');
- }
- /**
- * 切换歌曲(下一首或上一首)
- * @param direction - 切换方向,'next' 或 'previous'
- */
- async changeTrack(direction: 'next' | 'previous'): Promise<void> {
- try {
- // 1. 设置手动歌曲切换标志,防止自动播放等逻辑干扰
- this.isManualSongChange = true;
- // 关键修复:切歌操作应该总是开始播放新歌曲,无论之前是什么状态
- // 这是用户的明确意图:切换到新歌曲并播放
- const shouldAutoPlayNewSong = true;
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: changeTrack - will auto-play new song: ${shouldAutoPlayNewSong}`);
- // 2. 检查服务是否已准备就绪
- if (!this.isAllServicesReady()) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: ${direction} - 服务未就绪,等待服务初始化完成`);
- const isReady = await this.waitForAllServicesReady(500, false);
- if (!isReady) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Services not ready for ${direction}, skipping`);
- // 统一处理方式:在服务未就绪时直接返回,避免抛出错误中断用户体验
- this.isManualSongChange = false;
- return;
- }
- }
- // **关键修复:在移动播放列表位置之前先获取并保存当前歌曲的播放位置**
- const oldSong = this.playlistModel.getCurrentSong();
- if (oldSong) {
- this.savePlaybackPosition(oldSong);
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 Saved old song position before track change: ${oldSong.name}`);
- }
- const playMode = this.stateModel.getState().playMode;
- let moved = false;
- // 3. 根据 direction 参数执行不同的逻辑
- if (direction === 'next') {
- if (this.playlistModel.hasNext(playMode)) {
- moved = this.playlistModel.moveToNext(playMode);
- }
- } else { // direction === 'previous'
- if (await this.playlistModel.hasPrevious(playMode)) {
- // 注意:'previous' 相关的操作是异步的,需要 await
- moved = await this.playlistModel.moveToPrevious(playMode);
- }
- }
- // 如果没有成功移动(列表到头/尾,或移动失败),则直接返回
- if (!moved) {
- this.isManualSongChange = false;
- return;
- }
- const newIndex = this.playlistModel.getCurrentIndex();
- const newSong = this.playlistModel.getCurrentSong();
- this.stateModel.updateCurrentIndex(newIndex);
- if (newSong) {
- this.stateModel.updateCurrentSong(newSong);
- }
- // 静默停止当前播放(不再保存位置,因为已经在上面保存了)
- await this.stopSilentlyWithoutSave();
- // 直接播放新歌曲,切歌操作总是自动播放
- await this.playNewSongDirectly(newSong, shouldAutoPlayNewSong);
- // 更新播放列表状态
- await this.updatePlaylistStateInModel();
- // 优化:立即更新卡片状态,使用微任务调度避免阻塞
- Promise.resolve().then(async () => {
- try {
- // 确保播放列表状态是最新的
- this.updatePlaylistStateInModelSync();
- // 强制更新所有桌面卡片状态(微任务优先级高于宏任务)
- await this.updateAllForms(true);
-
- // 移除此处的AVSession更新,避免与onPrepared中的更新冲突
- // AVSession会在onPrepared回调中正确更新
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Widget state updated after track change');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update widgets after track change: ${error}`);
- }
- });
- } catch (error) {
- await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
- } finally {
- // 5. 在任何情况下都清除手动切换标志
- setTimeout(() => {
- this.isManualSongChange = false;
- }, 1000); // 延迟1秒清除,确保播放器状态稳定
- }
- }
- async playSongAtIndex(index: number): Promise<void> {
- console.log("Heanup 开始执行playSongAtIndex方法,播放指定索引的歌:"+index)
- try {
- // 设置手动歌曲切换标志,防止自动播放干扰
- this.isManualSongChange = true;
- const success = this.playlistModel.playSongAtIndex(index);
- if (!success) {
- this.isManualSongChange = false;
- throw new Error(`Invalid song index: ${index}`);
- }
- this.stateModel.updateCurrentIndex(index);
- // 静默停止当前播放并开始新歌曲(不触发状态变化通知)
- await this.stopSilently();
- // 在随机播放模式下,确保当前歌曲被添加到播放历史
- const playMode = this.stateModel.getState().playMode;
- if (playMode === PlayMode.RANDOM) {
- await this.playlistModel.ensureCurrentSongInHistory();
- }
- // 直接播放新歌曲,而不是恢复播放
- const newCurrentSong = this.playlistModel.getCurrentSong();
- await this.playNewSongDirectly(newCurrentSong, true); // 直接点击播放,应该自动开始
- // 更新状态模型中的播放列表状态
- await this.updatePlaylistStateInModel();
- // 通知歌曲变化
- if (newCurrentSong) {
- this.stateModel.updateCurrentSong(newCurrentSong);
- }
- // 异步预缓存相邻歌曲的卡片数据
- setTimeout(() => {
- this.updateAllForms();
- }, 0);
- } catch (error) {
- throw new Error;
- } finally {
- // 确保在任何情况下都清除手动切换标志
- setTimeout(() => {
- this.isManualSongChange = false;
- }, 1000); // 延迟1秒清除,确保播放器状态稳定
- }
- }
- // 状态查询方法
- getCurrentState(): PlayerState {
- return this.stateModel.getState();
- }
- /**
- * 获取播放器实际播放状态(最准确)
- * 直接从ijkPlayer获取,不依赖状态模型
- */
- getActualPlayingState(): boolean {
- try {
- const ijkPlayer = this.playerManager?.getIjkPlayer();
- if (ijkPlayer) {
- return ijkPlayer.isPlaying();
- }
- return false;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to get actual playing state: ${error}`);
- return false;
- }
- }
- getCurrentSong(): VideoItem | null {
- return this.playlistModel.getCurrentSong();
- }
- getFav(): Array<VideoItem> {
- if (this.favList.length > 0) {
- return this.favList;
- }
- // 如果缓存为空,异步更新但返回空数组,避免阻塞
- this.getTable().queryByisFav(1, async (result: VideoItem[]) => {
- this.favList = result;
- // 更新收藏列表后,重新更新AVSession状态以反映正确的收藏状态
- if (result.length > 0) {
- setTimeout(() => {
- this.updateSessionPlayState();
- }, 100);
- }
- })
- return this.favList;
- }
- /**
- * 获取当前歌曲的收藏状态
- */
- private getCurrentSongFavoriteState(): boolean {
- try {
- let currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: 当前歌曲为空`);
- return false;
- }
- let favList = this.getFav();
- if (!favList || favList.length === 0) {
- // 如果收藏列表为空,可能还在加载中,先返回false
- // 但不记录为错误,因为这是正常的初始化状态
- return false;
- }
- // 检查当前歌曲是否在收藏列表中
- for (let i = 0; i < favList.length; i++) {
- if (favList[i].filePath === currentSong.filePath && favList[i].isFav === 1) {
- return true;
- }
- }
- return false;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to get favorite state: ${error}`);
- return false;
- }
- }
- /**
- * 初始化收藏列表
- */
- private async initializeFavoriteList(): Promise<void> {
- try {
- return new Promise<void>((resolve) => {
- this.getTable().queryByisFav(1, (result: VideoItem[]) => {
- this.favList = result;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Favorite list initialized with ${this.favList.length} items`);
- resolve();
- });
- });
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to initialize favorite list: ${error}`);
- }
- }
- /**
- * 更新收藏列表
- */
- public async updateFavoriteList(): Promise<void> {
- try {
- return new Promise<void>((resolve) => {
- this.getTable().queryByisFav(1, (result: VideoItem[]) => {
- this.favList = result;
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Favorite list updated with ${this.favList.length} items`);
- // 更新收藏列表后,立即更新AVSession状态以反映正确的收藏状态
- setTimeout(() => {
- this.updateSessionPlayState();
- this.updateAllForms();
- }, 100);
- resolve();
- });
- });
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update favorite list: ${error}`);
- }
- }
- /**
- * 强制刷新收藏状态(用于收藏/取消收藏操作后)
- */
- public async refreshFavoriteStatus(): Promise<void> {
- try {
- // 清空缓存,强制重新加载
- this.favList = [];
- await this.updateFavoriteList();
- // 触发状态变化通知,确保LocalMusic UI能收到收藏状态变化
- this.stateModel.forceNotifyStateChanged();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Favorite status refreshed and state change notified');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to refresh favorite status: ${error}`);
- }
- }
- private getTable(): MediaTable {
- if (this.table) {
- return this.table;
- }
- if (this.context) {
- this.table = new MediaTable(this.context);
- return this.table;
- }
- return new MediaTable(getContext(this) as common.UIAbilityContext);
- }
- getPlaylist(): VideoItem[] {
- return this.playlistModel.getSongs();
- }
- getCurrentIndex(): number {
- return this.playlistModel.getCurrentIndex();
- }
- /**
- * 检查数据是否已从持久化存储恢复
- */
- isDataRestorationCompleted(): boolean {
- return this.isDataRestored;
- }
- /**
- * 等待数据恢复完成
- */
- async waitForDataRestoration(maxWaitMs: number = 3000): Promise<boolean> {
- const startTime = Date.now();
- while (!this.isDataRestored && (Date.now() - startTime) < maxWaitMs) {
- await new Promise<void>(resolve => setTimeout(resolve, 100));
- }
- return this.isDataRestored;
- }
- /**
- * 强制触发数据恢复(用于桌面卡片冷启动场景)
- */
- async forceDataRestoration(): Promise<boolean> {
- try {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Force data restoration started');
- // 如果数据已经恢复,直接返回
- if (this.isDataRestored) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Data already restored');
- return true;
- }
- // 强制执行数据恢复
- await this.restorePersistedState();
- // 如果播放列表仍为空,尝试从内存快速恢复
- if (this.playlistModel.getTotalCount() === 0) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Playlist empty, trying memory restore');
- await this.fastRestoreFromMemory();
- }
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Force restoration completed, playlist size: ${this.playlistModel.getTotalCount()}`);
- return this.isDataRestored;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Force data restoration failed: ${error}`);
- return false;
- }
- }
- /**
- * 检查服务是否已初始化
- */
- isServiceInitialized(): boolean {
- return this.initialized;
- }
- /**
- * 检查所有相关服务是否已准备就绪
- * 用于桌面卡片控制等场景,确保在执行播放控制前服务状态正常
- */
- isAllServicesReady(): boolean {
- if (this.hasChecked){
- return true;
- }else {
- this.hasChecked = true;
- try {
- // 检查基本初始化状态
- if (!this.initialized) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Service not initialized');
- return false;
- }
- // 检查核心组件是否就绪
- if (!this.playerManager || !this.stateModel || !this.playlistModel) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Core components not ready');
- return false;
- }
- // 检查 PlayerManager 是否已经初始化
- const playerInstance = this.playerManager.getIjkPlayer();
- if (!playerInstance) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Player instance not ready');
- return false;
- }
- // 检查上下文是否可用
- if (!this.context) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Context not available');
- return false;
- }
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: All services ready');
- return true;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Service readiness check failed: ${error}`);
- return false;
- }
- }
- }
- /**
- * 验证初始化完整性
- * 在应用启动后调用,确保所有关键组件都已正确初始化
- */
- validateInitialization(): InitializationValidation {
- const errors: string[] = [];
- const warnings: string[] = [];
- try {
- // 检查基础初始化
- if (!this.initialized) {
- errors.push('Service not initialized');
- }
- // 检查核心组件
- if (!this.playerManager) {
- errors.push('PlayerManager not available');
- } else {
- const playerInstance = this.playerManager.getIjkPlayer();
- if (!playerInstance) {
- warnings.push('Player instance not ready (may initialize later)');
- }
- }
- if (!this.stateModel) {
- errors.push('StateModel not available');
- }
- if (!this.playlistModel) {
- errors.push('PlaylistModel not available');
- }
- if (!this.context) {
- warnings.push('Context not set (may be set later)');
- }
- // 检查数据持久化服务
- if (!this.dataPersistence) {
- errors.push('DataPersistence service not available');
- }
- if (!this.playlistSync) {
- errors.push('PlaylistSync service not available');
- }
- // 检查错误恢复策略
- if (!this.errorRecovery) {
- warnings.push('ErrorRecovery strategy not available');
- }
- const isValid = errors.length === 0;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService validation: ${isValid ? 'VALID' :
- 'INVALID'}, errors: ${errors.length}, warnings: ${warnings.length}`);
- const result: InitializationValidation = {
- isValid,
- errors,
- warnings
- };
- return result;
- } catch (error) {
- errors.push(`Validation failed with exception: ${error}`);
- const result: InitializationValidation = {
- isValid: false,
- errors,
- warnings
- };
- return result;
- }
- }
- /**
- * 获取服务就绪状态的详细信息
- * 用于调试和用户反馈
- */
- getServiceReadinessInfo(): ServiceReadinessInfo {
- const playerInstance = this.playerManager?.getIjkPlayer();
- const playlistSize = this.playlistModel?.getTotalCount() || 0;
- const currentIndex = this.playlistModel?.getCurrentIndex() || -1;
- const details: ServiceReadinessDetails = {
- initialized: this.initialized,
- hasPlayerInstance: !!playerInstance,
- hasContext: !!this.context,
- dataRestored: this.isDataRestored,
- playlistSize: playlistSize,
- currentIndex: currentIndex
- };
- const isReady = this.isAllServicesReady();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService readiness: ${JSON.stringify(details)}`);
- const result: ServiceReadinessInfo = { isReady, details };
- return result;
- }
- /**
- * 等待所有服务准备就绪
- * @param maxWaitMs 最大等待时间(毫秒)
- * @param checkDataRestore 是否同时等待数据恢复完成
- */
- async waitForAllServicesReady(maxWaitMs: number = 5000, checkDataRestore: boolean = true): Promise<boolean> {
- const startTime = Date.now();
- const checkInterval = 100; // 每100ms检查一次
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Waiting for services ready (maxWait: ${maxWaitMs}ms, checkDataRestore: ${checkDataRestore})`);
- while ((Date.now() - startTime) < maxWaitMs) {
- // 检查基础服务就绪状态
- if (this.isAllServicesReady()) {
- // 如果不需要检查数据恢复,直接返回成功
- if (!checkDataRestore) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: All services ready (data restore check skipped)');
- return true;
- }
- // 检查数据恢复状态
- if (this.isDataRestored) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: All services ready and data restored');
- return true;
- }
- // 检查播放列表是否有数据(即使数据恢复标识未设置)
- const playlistSize = this.playlistModel.getTotalCount();
- if (playlistSize > 0) {
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Services ready with playlist data (${playlistSize} songs)`);
- return true;
- }
- }
- // 短暂等待后继续检查
- await new Promise<void>(resolve => setTimeout(resolve, checkInterval));
- }
- // 超时检查最终状态
- const finalCheck = this.isAllServicesReady();
- const playlistSize = this.playlistModel?.getTotalCount() || 0;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Service readiness timeout - finalCheck: ${finalCheck}, playlistSize: ${playlistSize}, dataRestored: ${this.isDataRestored}`);
- return finalCheck;
- }
- // 播放模式控制
- setPlayMode(mode: number): void {
- const playMode = mode as PlayMode;
- this.stateModel.updatePlayMode(playMode);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Play mode set to ${mode}`);
- }
- getPlayMode(): number {
- return this.stateModel.getState().playMode;
- }
- // 音量和速度控制
- setVolume(volume: number): void {
- const volumeStr = volume.toString();
- this.playerManager.setVolume(volumeStr, volumeStr);
- this.stateModel.updateVolume(volume);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Volume set to ${volume}`);
- }
- setPlaybackSpeed(speed: number): void {
- const speedStr = speed.toString() + 'f';
- this.playerManager.setPlaybackSpeed(speedStr);
- this.stateModel.updateSpeed(speed);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Speed set to ${speed}`);
- }
- // 播放列表管理
- setPlaylist(songs: VideoItem[], currentIndex: number = 0): void {
- // 记录调用栈信息以便调试
- const stack = new Error().stack || 'No stack available';
- const caller = stack.split('\n')[2] || 'Unknown caller';
- this.playlistModel.replaceSongs(songs, currentIndex);
- this.stateModel.updateCurrentIndex(currentIndex);
- // 更新状态模型中的播放列表状态
- this.updatePlaylistStateInModelSync();
- // 更新当前歌曲
- const currentSong = this.playlistModel.getCurrentSong();
- this.stateModel.updateCurrentSong(currentSong);
- // 同步到持久化存储(异步执行,不阻塞主线程)
- this.syncPlaylistToStorage().catch((error:Error) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService setPlaylist syncPlaylistToStorage error: ${error}`);
- });
- // 更新卡片显示播放列表变化
- this.updateWidgetsForPlaylistChange();
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Playlist set with ${songs.length} songs, caller: ${caller.trim()}`);
- // 如果设置的是小播放列表,记录更多信息
- if (songs.length <= 20) {
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Small playlist detected, caller stack: ${stack.substring(0, 500)}`);
- }
- }
- addToPlaylist(song: VideoItem, index?: number): void {
- this.playlistModel.addSong(song, index);
- // 更新状态模型中的播放列表状态
- this.updatePlaylistStateInModelSync();
- // 同步到持久化存储(异步执行,不阻塞主线程)
- this.syncPlaylistToStorage().catch((error:Error) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService addToPlaylist syncPlaylistToStorage error: ${error}`);
- });
- // 更新卡片显示播放列表变化
- this.updateWidgetsForPlaylistChange();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Song added to playlist`);
- }
- removeFromPlaylist(index: number): VideoItem | null {
- const removedSong = this.playlistModel.removeSong(index);
- if (removedSong) {
- this.stateModel.updateCurrentIndex(this.playlistModel.getCurrentIndex());
- // 更新状态模型中的播放列表状态
- this.updatePlaylistStateInModelSync();
- // 更新当前歌曲
- const currentSong = this.playlistModel.getCurrentSong();
- this.stateModel.updateCurrentSong(currentSong);
- // 同步到持久化存储(异步执行,不阻塞主线程)
- this.syncPlaylistToStorage().catch((error:Error) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService removeFromPlaylist syncPlaylistToStorage error: ${error}`);
- });
- // 更新卡片显示播放列表变化
- this.updateWidgetsForPlaylistChange();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Song removed from playlist at index ${index}`);
- }
- return removedSong;
- }
- /**
- * 同步播放列表到服务
- * 从LocalMusic迁移的方法,用于统一调用
- */
- syncPlaylistToService(songList: VideoItem[], currentIndex: number, playType: PlayMode): void {
- try {
- if (songList && songList.length > 0) {
- // 记录调用栈信息以便调试
- const stack = new Error().stack || 'No stack available';
- const caller = stack.split('\n')[2] || 'Unknown caller';
- this.setPlaylist(songList, currentIndex);
- // 同步播放模式到UnifiedPlayerService
- this.setPlayMode(playType);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Playlist and play mode synced - ${songList.length} songs, mode: ${playType}, caller: ${caller.trim()}`);
- // 如果同步的是小播放列表,记录更多信息
- if (songList.length <= 20) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Small playlist sync detected, first few songs: ${songList.slice(0, 5).map(s => s.name).join(', ')}`);
- }
- } else {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: syncPlaylistToService - No songs to sync');
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService syncPlaylistToService error: ${error}`);
- }
- }
- // 事件监听
- addStateListener(listener: PlayerStateListener): void {
- this.stateModel.addStateListener(listener);
- }
- removeStateListener(listener: PlayerStateListener): void {
- this.stateModel.removeStateListener(listener);
- }
- // 清理资源
- async release(): Promise<void> {
- try {
- this.stopProgressTimer();
- // 清理防抖定时器
- if (this.avSessionUpdateTimer !== -1) {
- clearTimeout(this.avSessionUpdateTimer);
- this.avSessionUpdateTimer = -1;
- }
- if (this.avMetadataUpdateTimer !== -1) {
- clearTimeout(this.avMetadataUpdateTimer);
- this.avMetadataUpdateTimer = -1;
- }
- // 关键修复:先保存当前状态,再清理资源
- // 这样可以确保播放列表在应用被杀掉时不会丢失
- await this.saveCurrentState();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Current state saved before resource cleanup');
- // 重要修复:确保在应用销毁时将播放状态设置为停止
- try {
- // 强制设置为停止状态,防止卡片显示错误的播放状态
- this.stateModel.updatePlayingState(false);
- this.stateModel.updateLoadingState(false);
-
- // 确保播放器也停止
- if (this.playerManager) {
- this.playerManager.release() ;
- }
-
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Forced stop state before release');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to force stop state: ${error}`);
- }
- if (this.avSessionController) {
- this.avSessionController.unregisterSessionListener();
- this.avSessionController = null;
- }
- // 清理同步监听器
- this.playlistSync.removeSyncListener(this);
- this.playlistSync.release();
- // 最后清理播放列表模型,确保在此之前状态已经保存
- this.playlistModel.clear();
- this.initialized = false;
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Resources released');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService release error: ${error}`);
- }
- }
- /**
- * 初始化AVSession
- * 按照官方文档的要求:创建 -> 注册控制命令 -> 设置元数据 -> 激活
- */
- private initializeAvSession(): void {
- try {
- this.avSessionController = AvSessionController.getInstance(false);
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession controller created');
- setTimeout(() => {
- this.setAvSessionListener();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession listeners configured');
- }, 500);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to initialize AVSession: ${error}`);
- }
- }
- /**
- * 设置AVSession监听器
- * 注意:控制命令已在AvSessionController中注册,这里设置实际的处理逻辑
- */
- private setAvSessionListener(): void {
- if (!this.avSessionController) {
- return;
- }
- const avSession = this.avSessionController.getAvSession();
- if (!avSession) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession not available for listener setup');
- return;
- }
- try {
- // 播放事件监听
- avSession.on('play', () => {
- this.startPlayOrResumePlay().catch((error: Error) => {
- LogUtils.getInstance().LOGI(`AVSession play command failed: ${error}`);
- });
- });
- // 暂停事件监听
- avSession.on('pause', () => {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession pause command received');
- this.pause().catch((error: Error) => {
- LogUtils.getInstance().LOGI(`AVSession pause command failed: ${error}`);
- });
- });
- // 停止事件监听
- avSession.on('stop', () => {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession stop command received');
- this.stop().catch((error: Error) => {
- LogUtils.getInstance().LOGI(`AVSession stop command failed: ${error}`);
- });
- });
- // 下一首事件监听
- avSession.on('playNext', () => {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession playNext command received');
- this.playNext().catch((error: Error) => {
- LogUtils.getInstance().LOGI(`AVSession playNext command failed: ${error}`);
- });
- });
- // 上一首事件监听
- avSession.on('playPrevious', () => {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession playPrevious command received');
- this.playPrevious().catch((error: Error) => {
- LogUtils.getInstance().LOGI(`AVSession playPrevious command failed: ${error}`);
- });
- });
- // 拖拽进度事件监听
- avSession.on('seek', (time: number) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: AVSession seek command received: ${time}ms`);
- this.seekTo(time.toString()).catch((error: Error) => {
- LogUtils.getInstance().LOGI(`AVSession seek command failed: ${error}`);
- });
- });
- // 播放模式设置监听
- avSession.on('setLoopMode', (mode: number) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: AVSession setLoopMode command received: ${mode}`);
- try {
- this.setPlayMode(mode);
- // 更新AvSession播放状态以反映新的播放模式
- this.updateSessionPlayState();
- // 同步更新桌面卡片
- this.updateAllForms();
- } catch (error) {
- LogUtils.getInstance().LOGI(`AVSession setLoopMode command failed: ${error}`);
- }
- });
- // 收藏事件监听
- avSession.on('toggleFavorite', () => {
- this.toggleFavorite() ;
- });
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession listeners set up successfully');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to set AVSession listeners: ${error}`);
- }
- }
- /**
- * 处理播放错误
- */
- private async handlePlaybackError(error: Error,
- errorType: PlayerErrorType = PlayerErrorType.PLAYBACK_ERROR): Promise<void> {
- try {
- // 创建播放器错误对象
- const playerError = new PlayerError(errorType, error.message);
- playerError.retryCount = this.currentRetryCount;
- // 创建错误上下文
- const context: ErrorContext = {
- currentSong: this.playlistModel.getCurrentSong(),
- playlist: this.playlistModel.getSongs(),
- currentIndex: this.playlistModel.getCurrentIndex(),
- retryCount: this.currentRetryCount,
- isNetworkAvailable: true // 这里可以实际检查网络状态
- };
- // 获取恢复策略
- const recoveryAction = await this.errorRecovery.handleError(playerError, context);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error recovery action: ${recoveryAction}`);
- // 执行恢复动作
- await this.executeRecoveryAction(recoveryAction, playerError, context);
- } catch (recoveryError) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error recovery failed: ${recoveryError}`);
- this.stateModel.notifyError(new PlayerError(PlayerErrorType.PLAYBACK_ERROR,
- `Recovery failed: ${(recoveryError as Error).message}`));
- }
- }
- /**
- * 执行错误恢复动作
- */
- private async executeRecoveryAction(action: ErrorRecoveryAction, error: PlayerError,
- context: ErrorContext): Promise<void> {
- switch (action) {
- case ErrorRecoveryAction.RETRY:
- await this.retryCurrentOperation(error);
- break;
- case ErrorRecoveryAction.SKIP_TO_NEXT:
- await this.skipToNextSong();
- break;
- case ErrorRecoveryAction.STOP_PLAYBACK:
- await this.stop();
- this.stateModel.notifyError(error);
- break;
- case ErrorRecoveryAction.WAIT_AND_RETRY:
- await this.waitAndRetry(error);
- break;
- case ErrorRecoveryAction.SHOW_ERROR:
- this.stateModel.notifyError(error);
- break;
- default:
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Unknown recovery action: ${action}`);
- this.stateModel.notifyError(error);
- }
- }
- /**
- * 重试当前操作
- */
- private async retryCurrentOperation(error: PlayerError): Promise<void> {
- try {
- this.currentRetryCount++;
- const delay = this.errorRecovery.getRetryDelay(this.currentRetryCount);
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Retrying operation in ${delay}ms (attempt ${this.currentRetryCount})`);
- // 等待指定时间后重试
- setTimeout(async () => {
- try {
- await this.startPlayOrResumePlay();
- this.currentRetryCount = 0; // 重试成功,重置计数
- } catch (retryError) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Retry failed: ${retryError}`);
- await this.handlePlaybackError(retryError as Error, error.type);
- }
- }, delay);
- } catch (retryError) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Retry setup failed: ${retryError}`);
- await this.skipToNextSong();
- }
- }
- /**
- * 跳到下一首歌曲
- */
- private async skipToNextSong(): Promise<void> {
- try {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Skipping to next song due to error');
- // 重置重试计数
- this.currentRetryCount = 0;
- // 检查是否有下一首
- const playMode = this.stateModel.getState().playMode;
- if (this.playlistModel.hasNext(playMode)) {
- await this.playNext();
- } else {
- // 没有下一首,停止播放
- await this.stop();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No next song available, stopping playback');
- }
- } catch (skipError) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to skip to next song: ${skipError}`);
- await this.stop();
- }
- }
- /**
- * 等待后重试
- */
- private async waitAndRetry(error: PlayerError): Promise<void> {
- try {
- this.currentRetryCount++;
- const delay = this.errorRecovery.getRetryDelay(this.currentRetryCount);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Waiting ${delay}ms before retry (network error)`);
- // 更新状态为加载中
- this.stateModel.updateLoadingState(true);
- setTimeout(async () => {
- try {
- await this.startPlayOrResumePlay();
- this.currentRetryCount = 0; // 重试成功,重置计数
- } catch (retryError) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Network retry failed: ${retryError}`);
- await this.handlePlaybackError(retryError as Error, PlayerErrorType.NETWORK_ERROR);
- }
- }, delay);
- } catch (waitError) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Wait and retry setup failed: ${waitError}`);
- await this.skipToNextSong();
- }
- }
- /**
- * 检查文件是否存在
- */
- private async checkFileExists(filePath: string): Promise<boolean> {
- try {
- // 这里可以添加文件存在性检查逻辑
- // 暂时返回true,实际实现需要使用文件系统API
- return true;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: File check failed: ${error}`);
- return false;
- }
- }
- /**
- * 检查网络连接
- */
- private async checkNetworkConnection(): Promise<boolean> {
- try {
- // 这里可以添加网络连接检查逻辑
- // 暂时返回true,实际实现需要使用网络API
- return true;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Network check failed: ${error}`);
- return false;
- }
- }
- // 私有辅助方法
- /**
- * 检查播放器是否需要重新设置
- */
- private async needsPlayerSetup(song: VideoItem): Promise<boolean> {
- try {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (!ijkPlayer) {
- console.log("Heanup2 UnifiedPlayerService: 播放器不存在,需要设置");
- return true;
- }
- // 检查播放器是否有数据源
- const duration = ijkPlayer.getDuration();
- if (duration <= 0) {
- console.log("Heanup2 UnifiedPlayerService: 播放器无数据源或时长无效,需要设置");
- return true;
- }
- // 检查当前数据源是否匹配(这里我们通过检查当前歌曲信息来判断)
- const currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong || currentSong.filePath !== song.filePath) {
- console.log(`Heanup2 UnifiedPlayerService: 歌曲文件路径不匹配,需要重新设置 (当前: ${currentSong?.filePath}, 目标: ${song.filePath})`);
- return true;
- }
- console.log(`Heanup2 UnifiedPlayerService: 播放器已设置正确的数据源,无需重新设置 (时长: ${duration}ms)`);
- return false;
- } catch (error) {
- console.log(`Heanup2 UnifiedPlayerService: 检查播放器设置状态时出错: ${error},默认需要设置`);
- return true;
- }
- }
- // 缓存播放器是否已初始化配置,避免重复设置
- private isPlayerConfigured: boolean = false;
- private async setupPlayerForSong(song: VideoItem): Promise<void> {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (!ijkPlayer) {
- throw new Error('Player not initialized');
- }
- console.log("Heanup2 UnifiedPlayerService: 开始重新设置播放器,这会重置所有状态");
- // 标记播放器未准备(因为要重新设置)
- this.isPlayerPreparedForCurrentSong = false;
- // 重置播放器 - 这会清除所有状态包括播放位置
- ijkPlayer.reset();
-
- // 清空当前加载的歌曲记录
- this.currentlyLoadedSong = null;
- // 重新设置音频模式 - 重置后需要重新设置
- ijkPlayer.setAudioId('unifiedPlayer');
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Reset audio mode for ${song.name}`);
- // 优化:只在首次或配置变更时重新设置所有选项
- if (!this.isPlayerConfigured) {
- this.playerManager.setupIjkPlayerOptions();
- this.isPlayerConfigured = true;
- } else {
- // 快速重置关键配置项,避免完整重新配置
- this.setupEssentialPlayerOptions();
- }
- // 设置音量(针对dsf格式特殊处理)
- const volume = this.stateModel.getState().volume;
- if (song.filePath.toLowerCase().endsWith('.dsf')) {
- this.playerManager.setVolume('1', '1');
- } else {
- this.playerManager.setVolume(volume.toString(), volume.toString());
- }
- // 设置数据源
- ijkPlayer.setDataSource(song.filePath);
-
- // 记录当前播放器加载的歌曲
- this.currentlyLoadedSong = song;
-
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Set data source to ${song.filePath}`);
- // 优化:异步设置HTTP请求头,不阻塞主流程
- Promise.resolve().then(() => {
- // 设置HTTP请求头
- const headers = new Map([
- ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (bbcallen@gmail.com)"],
- ["referer", "https://www.bilibili.com"]
- ]);
- ijkPlayer.setDataSourceHeader(headers);
- // 设置播放速度
- const speed = this.stateModel.getState().speed;
- this.playerManager.setPlaybackSpeed(speed.toString() + 'f');
- });
- // 设置消息监听器(重要:用于处理播放器内部事件)
- ijkPlayer.setMessageListener();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Player setup for song ${song.name}`);
- console.log("Heanup2 UnifiedPlayerService: 播放器重新设置完成,准备开始新的播放");
- }
- /**
- * 快速设置关键播放器选项,避免完整重新配置的性能开销
- */
- private setupEssentialPlayerOptions(): void {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (!ijkPlayer) return;
- // 仅设置切换歌曲时必须重置的关键选项
- ijkPlayer.native_setup(); // 音频模式初始化
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", "0");
- ijkPlayer.setScreenOnWhilePlaying(true);
-
- // 关键修复:确保变速播放配置在每次歌曲切换时都被设置
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", "1");
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Essential soundtouch configured for speed control');
- }
- private setupProgressTimer(): void {
- this.progressTimer = setInterval(() => {
- this.updateProgress();
- }, 1000); // 每秒更新一次进度
- }
- private startProgressTimer(): void {
- // 先停止现有的定时器,避免重复启动
- this.stopProgressTimer();
- // 启动新的定时器
- this.setupProgressTimer();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Progress timer started');
- }
- /**
- * 停止进度定时器
- */
- private stopProgressTimer(): void {
- if (this.progressTimer !== -1) {
- clearInterval(this.progressTimer);
- this.progressTimer = -1;
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Progress timer stopped');
- }
- }
- private updateProgress(): void {
- try {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- const currentState = this.stateModel.getState();
- // 只有在播放器存在、正在播放且已准备好时才更新进度
- if (!ijkPlayer || !currentState.isPlaying || !this.isPlayerPreparedForCurrentSong) {
- return;
- }
- const currentPosition = ijkPlayer.getCurrentPosition();
- const duration = ijkPlayer.getDuration();
- // 确保获取到有效的位置信息
- if (currentPosition >= 0 && duration > 0) {
- this.stateModel.updateProgress(currentPosition, duration);
- this.updateSessionPlayState();
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updateProgress error: ${error}`);
- }
- }
- getCurrentPosition(): number {
- try {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (!ijkPlayer) {
- return 0;
- }
- return ijkPlayer.getCurrentPosition();
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService getCurrentPosition error: ${error}`);
- return 0;
- }
- }
- getIjkPlayer(): IjkMediaPlayer | null {
- try {
- return this.playerManager.getIjkPlayer();
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService getIjkPlayer error: ${error}`);
- return null;
- }
- }
- // 记录当前播放器实际加载的歌曲
- private currentlyLoadedSong: VideoItem | null = null;
- private savePlaybackPosition(targetSong?: VideoItem): void {
- try {
- // 检查用户是否启用了记忆播放功能
- const isMusicMemoryPlay = PuraPreferencesUtil.getBooleanSync(SettingPage.iS_MUSIC_MEMORY_PLAY, false);
- if (!isMusicMemoryPlay) {
- return; // 用户未启用,直接返回
- }
- const ijkPlayer = this.playerManager.getIjkPlayer();
- let currentSong = targetSong;
-
- // 如果没有传入具体歌曲,优先使用播放器当前加载的歌曲
- if (!currentSong) {
- currentSong = this.currentlyLoadedSong || this.playlistModel.getCurrentSong()!;
- }
-
- if (ijkPlayer != null && currentSong != null) {
- const position = ijkPlayer.getCurrentPosition();
- const duration = ijkPlayer.getDuration();
- const threshold = 5000; // 阈值,单位为毫秒(这里设为5秒)
- // 如果播放位置接近视频末尾,则保存 position 为 0
- const playbackPosition = (duration - position < threshold) ? 0 : position;
-
- // 使用filePath作为键,确保与恢复时一致
- PuraPreferencesUtil.putSync(currentSong.filePath, playbackPosition);
-
- // 同时保存到AppStorage,确保卡片也能访问(使用相同的键)
- AppStorage.setOrCreate(`playback_${currentSong.filePath}`, playbackPosition);
-
- // 获取调用栈信息用于调试
- const stack = new Error().stack;
- const caller = stack?.split('\n')[2]?.trim() || 'Unknown caller';
-
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 Saved playback position: ${playbackPosition}ms for ${currentSong.name} (filePath: ${currentSong.filePath}) - Called from: ${caller}`);
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 savePlaybackPosition error: ${error}`);
- }
- }
- private async restorePlaybackPosition(song: VideoItem): Promise<void> {
- // const position: number = PreferencesUtil.getNumberSync(this.videoUrl, 0);
- // // ToastUtil.showToast('position = ' + position)
- // if (this.mIjkMediaPlayer != null && position > 0) {
- // // ToastUtil.showToast('seekTo = ' + position)
- // this.seekTo(position + "");
- //
- // }
- if (song != null) {
- const position: number = PuraPreferencesUtil.getNumberSync(song.filePath, 0);
- if (position > 0) {
- await this.playerManager.seekToPosition(position.toString());
- }
- }
- // try {
- // // 使用DataPersistenceService恢复播放进度
- // const progressData = await this.dataPersistence.loadPlaybackProgress(song.filePath);
- // if (progressData && progressData.position > 0 && !progressData.completed) {
- // await this.playerManager.seekToPosition(progressData.position.toString());
- // LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Restored playback position ${progressData.position}ms for ${song.name}`);
- // }
- // } catch (error) {
- // LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService restorePlaybackPosition error: ${error}`);
- // }
- }
- // ==================== 数据持久化和同步方法 ====================
- /**
- * 恢复持久化的状态
- */
- /**
- * 快速从内存中恢复关键数据(AppStorage)
- */
- private async fastRestoreFromMemory(): Promise<void> {
- try {
- console.log("Heanup2 UnifiedPlayerService: 开始从内存快速恢复关键数据");
- // 从AppStorage快速获取播放列表数据
- const playlistData = AppStorage.get<PlaylistData>('player_playlist');
- if (playlistData && playlistData.songs.length > 0) {
- console.log(`Heanup2 UnifiedPlayerService: 从内存快速恢复播放列表,歌曲数量=${playlistData.songs.length}`);
- this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
- this.stateModel.updateCurrentIndex(playlistData.currentIndex);
- this.stateModel.updatePlayMode(playlistData.playMode);
- // 恢复当前歌曲
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- this.stateModel.updateCurrentSong(currentSong);
- console.log(`Heanup2 UnifiedPlayerService: 快速恢复当前歌曲: ${currentSong.name}`);
- }
- }
- // 从AppStorage快速获取播放状态数据
- const stateData = AppStorage.get<PlayerStateData>('player_state') as PlayerStateData;
- if (stateData) {
- console.log(`Heanup2 UnifiedPlayerService: 从内存快速恢复播放状态 - playing: ${stateData.isPlaying}, index: ${stateData.currentIndex}`);
- this.stateModel.updateVolume(stateData.volume);
- this.stateModel.updateSpeed(stateData.speed);
- this.stateModel.updatePlayMode(stateData.playMode);
- this.stateModel.updatePlayingState(stateData.isPlaying || false);
- if (stateData.currentIndex >= 0) {
- this.stateModel.updateCurrentIndex(stateData.currentIndex);
- }
- }
- console.log("Heanup2 UnifiedPlayerService: 内存数据快速恢复完成");
- // 设置一个临时的数据恢复完成标识,允许快速响应
- this.isDataRestored = true;
- // 立即更新播放列表状态,确保上一首/下一首状态正确
- this.updatePlaylistStateInModelSync();
- } catch (error) {
- console.log(`Heanup2 UnifiedPlayerService: 快速恢复内存数据失败: ${error}`);
- }
- }
- private async restorePersistedState(): Promise<void> {
- try {
- console.log("Heanup2 UnifiedPlayerService: 开始快速恢复持久化状态");
- // 并行加载播放列表和播放状态,提高效率
- const loadResults = await Promise.all([
- this.dataPersistence.loadPlaylist().catch(() => null),
- this.dataPersistence.loadPlayerState().catch(() => null)
- ]);
- const playlistData = loadResults[0];
- const stateData = loadResults[1];
- // 恢复播放列表
- if (playlistData && playlistData.songs.length > 0) {
- console.log(`Heanup2 UnifiedPlayerService: 恢复播放列表,歌曲数量=${playlistData.songs.length}, 当前索引=${playlistData.currentIndex}`);
- this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
- this.stateModel.updateCurrentIndex(playlistData.currentIndex);
- this.stateModel.updatePlayMode(playlistData.playMode);
- // 恢复当前歌曲到状态模型
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- this.stateModel.updateCurrentSong(currentSong);
- console.log(`Heanup2 UnifiedPlayerService: 恢复当前歌曲: ${currentSong.name}`);
- }
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Restored playlist with ${playlistData.songs.length} songs`);
- // 立即更新播放列表状态,确保上一首/下一首状态正确
- this.updatePlaylistStateInModelSync();
- } else {
- console.log("Heanup2 UnifiedPlayerService: 没有找到保存的播放列表或播放列表为空");
- }
- // 恢复播放状态
- if (stateData) {
- console.log(`Heanup2 UnifiedPlayerService: 加载到的播放状态 - playing: ${stateData.isPlaying}, paused: ${stateData.isPaused}, index: ${stateData.currentIndex}`);
- // 批量更新状态,减少回调次数
- this.stateModel.updateVolume(stateData.volume);
- this.stateModel.updateSpeed(stateData.speed);
- this.stateModel.updatePlayMode(stateData.playMode);
- // 恢复播放状态(UI会使用实际播放器状态,所以这里可以恢复逻辑状态)
- this.stateModel.updatePlayingState(stateData.isPlaying || false);
- // 恢复当前播放索引
- if (stateData.currentIndex >= 0) {
- this.stateModel.updateCurrentIndex(stateData.currentIndex);
- }
- console.log("Heanup2 UnifiedPlayerService: 播放状态恢复完成");
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Restored player state - playing: ${stateData.isPlaying}, paused: ${stateData.isPaused}, index: ${stateData.currentIndex}`);
- } else {
- console.log("Heanup2 UnifiedPlayerService: 没有找到保存的播放状态");
- }
- console.log("Heanup2 UnifiedPlayerService: 持久化状态快速恢复完成");
- // 设置数据恢复完成标识
- this.isDataRestored = true;
- // 立即更新播放列表状态,确保上一首/下一首状态正确
- this.updatePlaylistStateInModelSync();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Data restoration completed');
- } catch (error) {
- console.log(`Heanup2 UnifiedPlayerService: 恢复持久化状态时出错: ${error}`);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService restorePersistedState error: ${error}`);
- // 即使出错也设置为已恢复,避免无限等待
- this.isDataRestored = true;
- }
- }
- /**
- * 保存当前状态
- */
- private async saveCurrentState(): Promise<void> {
- try {
- // 获取当前状态
- const currentState = this.stateModel.getState();
- // 验证播放器实际状态,确保状态模型与实际播放器状态一致
- try {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- const actualPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
- // 如果状态不一致,以实际播放器状态为准
- // 但在暂停操作期间跳过检测,避免误判
- if (currentState.isPlaying !== actualPlaying && !this.isPausingInProgress) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: State mismatch detected during save - StateModel: ${currentState.isPlaying}, Actual: ${actualPlaying}`);
- this.stateModel.updatePlayingState(actualPlaying);
- // 重新获取修正后的状态
- const correctedState = this.stateModel.getState();
- this.dataPersistence.savePlayerState(correctedState);
- } else {
- if (this.isPausingInProgress && currentState.isPlaying !== actualPlaying) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Skipping state mismatch check during pause operation - StateModel: ${currentState.isPlaying}, Actual: ${actualPlaying}`);
- }
- this.dataPersistence.savePlayerState(currentState);
- }
- } catch (playerError) {
- // 如果获取播放器状态失败,默认保存为停止状态
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to get actual player state, saving as stopped: ${playerError}`);
- this.stateModel.updatePlayingState(false);
- const stoppedState = this.stateModel.getState();
- this.dataPersistence.savePlayerState(stoppedState);
- }
- // 修复:只在播放位置有意义时才保存,避免在歌曲刚开始时覆盖记忆播放位置
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (ijkPlayer && this.isPlayerPreparedForCurrentSong) {
- const currentPosition = ijkPlayer.getCurrentPosition();
- // 只有当播放位置大于2秒时才保存,避免覆盖记忆播放位置
- if (currentPosition > 2000) {
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- this.savePlaybackPosition(currentSong);
- }
- } else {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Skipping playback position save - position too small (${currentPosition}ms)`);
- }
- }
-
- await this.syncPlaylistToStorage();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Current state saved successfully`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService saveCurrentState error: ${error}`);
- }
- }
- /**
- * 外部调用保存当前状态的方法(供EntryAbility使用)
- */
- public async saveCurrentStateExternal(): Promise<void> {
- await this.saveCurrentState();
- }
- /**
- * 同步播放列表到存储
- */
- private async syncPlaylistToStorage(): Promise<void> {
- try {
- const playlist = this.playlistModel.getSongs();
- const currentIndex = this.playlistModel.getCurrentIndex();
- const playMode = this.stateModel.getState().playMode;
- // 获取调用栈信息用于调试
- const stack = new Error().stack;
- const caller = stack?.split('\n')[2]?.trim() || 'Unknown caller';
-
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: syncPlaylistToStorage called - playlist length: ${playlist?.length || 0}, currentIndex: ${currentIndex}, playMode: ${playMode}, caller: ${caller}`);
- // 总是尝试保存播放列表状态,包括空播放列表的情况
- // 这样可以确保用户关闭应用时的真实状态被正确保存
- await this.playlistSync.syncPlaylistToStorage(playlist || [], currentIndex, playMode);
-
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: syncPlaylistToStorage completed successfully`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService syncPlaylistToStorage error: ${error}`);
- }
- }
- // ==================== PlaylistSyncListener 接口实现 ====================
- /**
- * 播放列表同步完成回调
- */
- onPlaylistSynced(playlist: VideoItem[], currentIndex: number, playMode: PlayMode): void {
- }
- /**
- * 播放列表加载完成回调
- */
- onPlaylistLoaded(playlistData: PlaylistData): void {
- try {
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Playlist loaded from storage - ${playlistData.songs.length} songs`);
- // 检查是否需要更新本地播放列表
- const currentPlaylist = this.playlistModel.getSongs();
- const currentIndex = this.playlistModel.getCurrentIndex();
- // 如果存储的数据更新,更新本地播放列表
- if (playlistData.songs.length !== currentPlaylist.length ||
- playlistData.currentIndex !== currentIndex) {
- this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
- this.stateModel.updateCurrentIndex(playlistData.currentIndex);
- this.stateModel.updatePlayMode(playlistData.playMode);
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Local playlist updated from storage');
- }
- // 更新数据恢复状态(如果还没有恢复的话)
- if (!this.isDataRestored) {
- this.isDataRestored = true;
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Data restoration completed via onPlaylistLoaded');
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onPlaylistLoaded error: ${error}`);
- }
- }
- /**
- * 自动同步执行回调
- */
- onAutoSyncPerformed(): void {
- try {
- // 定期保存当前状态(异步执行,不等待完成)
- this.saveCurrentState().catch((error:Error) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onAutoSyncPerformed saveCurrentState error: ${error}`);
- });
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onAutoSyncPerformed error: ${error}`);
- }
- }
- /**
- * 同步错误回调
- */
- onSyncError(error: Error): void {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService sync error: ${error.message}`);
- }
- // ==================== 公共数据访问方法 ====================
- /**
- * 获取数据持久化服务实例
- */
- getDataPersistenceService(): IDataPersistenceService {
- return this.dataPersistence;
- }
- /**
- * 获取播放列表同步服务实例
- */
- getPlaylistSyncService(): PlaylistSyncService {
- return this.playlistSync;
- }
- // ==================== StateChangeCallback 接口实现 ====================
- /**
- * 歌曲变化回调(来自StateSyncService的本地通知)
- */
- onSongChanged?(song: VideoItem): void {
- try {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Received song change notification - ${song.name}`);
- // 这里可以处理来自其他组件的歌曲变化通知
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onSongChanged error: ${error}`);
- }
- }
- // ==================== WidgetControlCallback 接口实现 ====================
- /**
- * 处理来自卡片的播放/暂停命令
- * @param params 包含卡片当前UI状态的对象
- */
- public async onPlayPause(params?: PlayPauseInterface): Promise<void> {
- try {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Received play/pause command from widget');
- const widgetIsPlaying = params?.widgetIsPlaying;
- if (widgetIsPlaying !== undefined) {
- // 新逻辑:信任卡片传递的状态
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Using widget state - widgetIsPlaying=${widgetIsPlaying}`);
- if (widgetIsPlaying) {
- // 卡片显示的是暂停按钮(UI为播放状态),所以执行暂停
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Widget state is PLAYING, will pause.');
- await this.pause();
- } else {
- // 卡片显示的是播放按钮(UI为暂停/停止状态),所以执行播放
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Widget state is PAUSED, will play.');
- await this.startPlayOrResumePlay();
-
- // 关键修复:播放开始后,确保播放器准备状态正确设置
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (ijkPlayer && ijkPlayer.isPlaying()) {
- this.isPlayerPreparedForCurrentSong = true;
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Widget play success - set player prepared state to true');
- }
- }
- } else {
- // 向后兼容的旧逻辑:根据播放器实际状态切换
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: widgetIsPlaying not provided, using fallback logic.');
- const isActuallyPlaying = this.getActualPlayingState();
- if (isActuallyPlaying) {
- await this.pause();
- } else {
- await this.startPlayOrResumePlay();
- }
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onPlayPause error: ${error}`);
- }
- }
- /**
- * 处理来自卡片的下一首命令
- */
- async onNextSong?(): Promise<void> {
- try {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Received next song command from widget');
- await this.playNext();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Next song command executed successfully');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onNextSong error: ${error}`);
- throw new Error;
- }
- }
- /**
- * 处理来自卡片的上一首命令
- */
- async onPreviousSong?(): Promise<void> {
- try {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Received previous song command from widget');
- await this.playPrevious();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Previous song command executed successfully');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onPreviousSong error: ${error}`);
- throw new Error;
- }
- }
- /**
- * 处理来自卡片的拖动进度命令
- */
- async onSeekTo?(position: number): Promise<void> {
- try {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Received seek to command from widget - position: ${position}`);
- await this.seekTo(position.toString());
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Seek to command executed successfully');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onSeekTo error: ${error}`);
- throw new Error;
- }
- }
- /**
- * 处理来自卡片的状态请求命令
- */
- async onStateRequest?(): Promise<void> {
- try {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Received state request from widget');
- await this.broadcastCurrentState();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: State request handled successfully');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService onStateRequest error: ${error}`);
- throw new Error;
- }
- }
- /**
- * 手动广播当前状态(用于响应卡片的状态请求)
- */
- async broadcastCurrentState(): Promise<void> {
- try {
- // 确保播放列表状态是最新的
- this.updatePlaylistStateInModelSync();
- // 触发状态监听器通知
- this.stateModel.forceNotifyStateChanged();
- // 更新桌面卡片
- await this.updateAllForms(true); // 强制更新
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService broadcastCurrentState error: ${error}`);
- throw new Error;
- }
- }
- // ==================== 私有辅助方法 ====================
- /**
- * 设置音频中断监听器
- */
- private setupAudioInterruptListener(): void {
- try {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (ijkPlayer) {
- ijkPlayer.on('audioInterrupt', (event: InterruptEvent) => {
- this.handleAudioInterrupt(event);
- });
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Audio interrupt listener set up successfully');
- } else {
- LogUtils.getInstance()
- .LOGI('Heanup UnifiedPlayerService: Failed to set up audio interrupt listener - no player instance');
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error setting up audio interrupt listener: ${(error as Error).message}`);
- }
- }
- /**
- * 处理音频中断事件
- */
- async handleAudioInterrupt(event: InterruptEvent): Promise<void> {
- try {
- LogUtils.getInstance().LOGI(`onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`);
- // 首先保存当前播放位置
- const currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- this.savePlaybackPosition(currentSong);
- }
- switch (event.hintType) {
- case InterruptHintType.INTERRUPT_HINT_PAUSE:
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Audio interrupt - pausing playback');
- await this.pause();
- break;
- case InterruptHintType.INTERRUPT_HINT_RESUME:
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Audio interrupt - resuming playback');
- await this.startPlayOrResumePlay();
- break;
- case InterruptHintType.INTERRUPT_HINT_STOP:
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Audio interrupt - stopping playback');
- await this.stop();
- break;
- case InterruptHintType.INTERRUPT_HINT_DUCK:
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Audio interrupt - ducking (lowering volume)');
- // 可以实现音量降低逻辑
- break;
- case InterruptHintType.INTERRUPT_HINT_UNDUCK:
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Audio interrupt - unducking (restoring volume)');
- // 可以实现音量恢复逻辑
- break;
- default:
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Unknown audio interrupt hint type: ${event.hintType}`);
- break;
- }
- // 通知状态变化
- await this.updatePlaylistStateInModel();
- await this.broadcastCurrentState();
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error handling audio interrupt: ${(error as Error).message}`);
- await this.handlePlaybackError(error as Error, PlayerErrorType.AUDIO_INTERRUPT_ERROR);
- }
- }
- /**
- * 更新状态模型中的播放列表状态
- */
- private async updatePlaylistStateInModel(): Promise<void> {
- try {
- const currentIndex = this.playlistModel.getCurrentIndex();
- const totalCount = this.playlistModel.getTotalCount();
- const playMode = this.stateModel.getState().playMode;
- // 在随机播放模式下,确保当前歌曲在播放历史中
- if (playMode === PlayMode.RANDOM) {
- await this.playlistModel.ensureCurrentSongInHistory();
- // 输出播放历史调试信息
- const debugInfo = this.playlistModel.getPlayHistoryDebugInfo();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Random mode play history - ${debugInfo}`);
- }
- const hasNext = this.playlistModel.hasNext(playMode);
- const hasPrevious = await this.playlistModel.hasPrevious(playMode);
- this.stateModel.updatePlaylistState(hasNext, hasPrevious, totalCount);
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Playlist state updated - hasNext=${hasNext}, hasPrevious=${hasPrevious}, totalCount=${totalCount}, playMode=${playMode}`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updatePlaylistStateInModel error: ${error}`);
- }
- }
- /**
- * 更新状态模型中的播放列表状态(同步版本)- 优化防抖
- */
- private updatePlaylistStateInModelSync(): void {
- try {
- const currentIndex = this.playlistModel.getCurrentIndex();
- const totalCount = this.playlistModel.getTotalCount();
- const playMode = this.stateModel.getState().playMode;
- const hasNext = this.playlistModel.hasNext(playMode);
- const hasPrevious = this.playlistModel.hasPreviousSync(playMode);
- // 检查状态是否真的发生了变化,避免频繁的无效更新
- const currentState = this.stateModel.getState();
- const stateChanged = (
- currentState.hasNext !== hasNext ||
- currentState.hasPrevious !== hasPrevious ||
- currentState.totalCount !== totalCount
- );
- if (stateChanged) {
- this.stateModel.updatePlaylistState(hasNext, hasPrevious, totalCount);
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Playlist state updated sync - hasNext=${hasNext}, hasPrevious=${hasPrevious}, totalCount=${totalCount}, playMode=${playMode}`);
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updatePlaylistStateInModelSync error: ${error}`);
- }
- }
- // ==================== PlayerStateCallback 实现 ====================
- onPrepared(): void {
- // 标记播放器已为当前歌曲准备就绪
- this.isPlayerPreparedForCurrentSong = true;
- console.log("Heanup2 UnifiedPlayerService: onPrepared - 播放器已为当前歌曲准备就绪");
- setTimeout(() => {
- // 执行跳过片头和记忆播放恢复逻辑
- this.checkAndHandleOnPreparedLogic();
- this.stateModel.updateLoadingState(false);
- const ijkPlayer = this.playerManager.getIjkPlayer();
- const isActuallyPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
- console.log(`Heanup2 UnifiedPlayerService: onPrepared - 实际播放状态: ${isActuallyPlaying}`);
- // 强制同步状态模型
- this.stateModel.updatePlayingState(isActuallyPlaying);
- // 确保播放列表状态是最新的
- this.updatePlaylistStateInModelSync();
- // 更新 AVSession 状态
- if (isActuallyPlaying) {
- // 优化:确保使用最新的歌曲数据
- let currentSong = this.playlistModel.getCurrentSong();
- if (currentSong) {
- console.log(`Heanup2 UnifiedPlayerService: onPrepared - updateAvSessionMetadata: ${json.stringify(currentSong)}`);
- // 立即更新,避免延迟导致的状态不一致
- this.updateAvSessionMetadata(currentSong);
- }
- this.updateSessionPlayState(true); // 强制更新,不使用防抖
- }
- // 延迟更新卡片,确保状态已同步
- setTimeout(() => {
- this.updateWidgetsForStateChange();
- }, 50);
- }, 100);
- // 播放器准备完成时立即更新卡片(移除加载状态)
- this.updateWidgetsForStateChange();
- }
- /**
- * 播放器准备完成后的特殊逻辑处理
- * 包括跳过片头和记忆播放恢复
- */
- private checkAndHandleOnPreparedLogic(): void {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: 跳过片头和记忆播放恢复 Checking onPrepared logic');
- try {
- const currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No current song, skipping onPrepared logic');
- return;
- }
- const videoUrl = currentSong.filePath;
- const duration = this.playerManager.getIjkPlayer()?.getDuration() || 0;
- // 检查跳过片头功能
- const jumpSettings = this.loadJumpSettings(currentSong);
-
- if (jumpSettings.isEnabled && jumpSettings.jumpTopTime > 0) {
- console.log(`Heanup UnifiedPlayerService: Jump settings enabled: ${jumpSettings.jumpTopTime}s`)
- if (jumpSettings.jumpTopTime * 1000 <= duration) {
- // 需要跳过片头
- if (videoUrl && !videoUrl.toLowerCase().endsWith('.ts')) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Jumping to intro end: ${jumpSettings.jumpTopTime}s`);
- this.seekTo((jumpSettings.jumpTopTime * 1000).toString());
- }
- } else {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Jump time exceeds video duration');
- }
- } else {
- // 没有跳过片头,检查记忆播放
- this.handleMemoryPlayback(currentSong, videoUrl);
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error in checkAndHandleOnPreparedLogic: ${error}`);
- }
- }
- /**
- * 加载跳过头尾设置
- */
- private loadJumpSettings(currentSong: VideoItem): JumpSettings {
- try {
- // 这里应该根据播放模式来获取设置,但因为我们在 UnifiedPlayerService 中,
- // 暂时从当前歌曲的文件路径获取文件夹设置
- const parentPath = this.getParentPath(currentSong.filePath);
-
- const jumpTopTime: number = PuraPreferencesUtil.getNumberSync(parentPath + 'jumpTopTime', 0);
- const jumpEndTime: number = PuraPreferencesUtil.getNumberSync(parentPath + 'jumpEndTime', 0);
- const isEnabled: boolean = PuraPreferencesUtil.getBooleanSync(parentPath + 'isOpenJump', false);
-
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Loaded jump settings - enabled: ${isEnabled}, top: ${jumpTopTime}s, end: ${jumpEndTime}s`);
-
- const settings: JumpSettings = { isEnabled, jumpTopTime, jumpEndTime };
- return settings;
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error loading jump settings: ${error}`);
- const defaultSettings: JumpSettings = { isEnabled: false, jumpTopTime: 0, jumpEndTime: 0 };
- return defaultSettings;
- }
- }
- /**
- * 处理记忆播放
- */
- private handleMemoryPlayback(currentSong: VideoItem, videoUrl: string): void {
- try {
- console.log(`Heanup 记忆播放功能: 处理记忆播放 for ${currentSong.name}`)
-
- // 检查用户是否启用了记忆播放功能
- const isMusicMemoryPlay: boolean = PuraPreferencesUtil.getBooleanSync(SettingPage.iS_MUSIC_MEMORY_PLAY, false);
- const isMemoryLastPlay: boolean = PuraPreferencesUtil.getBooleanSync(SettingPage.iS_MEMORY_LAST_PLAY, false);
-
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能: Settings - isMusicMemoryPlay: ${isMusicMemoryPlay}, isMemoryLastPlay: ${isMemoryLastPlay}`);
-
- // 只在首次启动时检查 isMemoryLastPlay
- if (!isMusicMemoryPlay && !isMemoryLastPlay) {
- LogUtils.getInstance().LOGI('Heanup 记忆播放功能 : Memory play disabled, skipping restore');
- return;
- }
- if (currentSong && currentSong.filePath && !currentSong.filePath.toLowerCase().endsWith('.ts')) {
- // 使用 currentSong.filePath 作为键,确保与保存时一致
- let position = PuraPreferencesUtil.getNumberSync(currentSong.filePath, 0);
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能: Retrieved position ${position}ms from key: ${currentSong.filePath}`);
-
- if (position > 0) {
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 : Restoring playback position: ${position}ms for ${currentSong.name}`);
- this.seekTo(position.toString());
- } else {
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 : No saved position found for ${currentSong.name} (key: ${currentSong.filePath})`);
- }
- } else {
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能: Skipping restore - invalid song or .ts file: ${currentSong?.filePath}`);
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup 记忆播放功能 : Error in handleMemoryPlayback: ${error}`);
- }
- }
- /**
- * 获取文件的父目录路径
- */
- private getParentPath(filePath: string): string {
- try {
- const lastSlashIndex = filePath.lastIndexOf('/');
- if (lastSlashIndex > 0) {
- return filePath.substring(0, lastSlashIndex + 1);
- }
- return filePath;
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error getting parent path: ${error}`);
- return filePath;
- }
- }
- onPlaybackStarted(): void {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Playback started (new song)');
- // 这个回调只应该在新歌曲开始播放时被调用(通过onPrepared触发)
- // 不应该在暂停恢复时被调用
- // 在随机播放模式下,确保当前歌曲在播放历史中
- const playMode = this.stateModel.getState().playMode;
- if (playMode === PlayMode.RANDOM) {
- this.playlistModel.ensureCurrentSongInHistory();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Ensured current song in play history for random mode');
- }
- // 立即更新播放状态
- this.stateModel.updatePlayingState(true);
- // 启动进度定时器
- this.startProgressTimer();
- // 保存播放状态变化
- this.saveCurrentState();
- // 立即更新系统播控状态,确保与播放状态同步
- this.updateSessionPlayState(true); // 强制更新,不使用防抖
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession state updated for new song playback');
- // 播放开始时更新卡片显示播放状态
- this.updateWidgetsForPlayStateChange();
- // 同步更新所有桌面卡片状态
- this.updateAllForms();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: New song playback started successfully');
- }
- onPlaybackCompleted(): void {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Playback completed');
-
- // **关键修复:在手动切换歌曲时,不要保存播放位置,因为已经在 changeTrack 中保存了**
- if (!this.isManualSongChange) {
- // 只有在非手动切换时才保存当前歌曲的播放位置(如自然播放完成)
- const completedSong = this.playlistModel.getCurrentSong();
- if (completedSong) {
- this.savePlaybackPosition(completedSong);
- }
- } else {
- LogUtils.getInstance().LOGI('Heanup 记忆播放功能: Skipping save in onPlaybackCompleted due to manual song change');
- }
-
- this.stateModel.updatePlayingState(false);
- this.stopProgressTimer();
- // 保存播放状态变化
- this.saveCurrentState();
- this.updateSessionPlayState();
- // 更新卡片显示播放完成状态
- this.updateWidgetsForPlayStateChange();
- // 同步更新所有桌面卡片状态
- this.updateAllForms();
- // 异步处理自动播放逻辑,避免阻塞主线程
- setTimeout(() => {
- this.handleAutoPlayOnCompletion().catch(() => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Auto-play error`);
- });
- }, 100); // 延迟100ms执行,确保当前回调完成
- }
- /**
- * 处理歌曲播放完成后的自动播放逻辑
- */
- private async handleAutoPlayOnCompletion(): Promise<void> {
- try {
- // 检查是否正在进行手动歌曲切换,如果是则跳过自动播放
- if (this.isManualSongChange) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Manual song change in progress, skipping auto-play');
- return;
- }
- // 防抖检查:避免快速连续的自动播放调用
- const currentTime = Date.now();
- if (currentTime - this.lastAutoPlayTime < this.autoPlayDebounceMs) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Auto-play debounced, skipping');
- return;
- }
- this.lastAutoPlayTime = currentTime;
- // 检查服务状态,避免在服务未就绪时执行自动播放
- if (!this.isAllServicesReady()) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Services not ready for auto-play, skipping');
- return;
- }
- const currentState = this.stateModel.getState();
- const playMode = currentState.playMode;
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Handling auto-play, mode: ${playMode}`);
- switch (playMode) {
- case PlayMode.SEQUENCE: // 0: 顺序播放/列表循环
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Auto-playing next song in sequence mode');
- // 检查是否有下一首歌曲
- if (this.playlistModel.hasNext(playMode)) {
- await this.playNext();
- } else {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No next song available in sequence mode');
- }
- break;
- case PlayMode.SINGLE_REPEAT: // 1: 单曲循环
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Repeating current song');
- // 重新播放当前歌曲
- await this.startPlayOrResumePlay();
- break;
- case PlayMode.NORMAL: // 2: 正常播放,播完停止
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Normal mode - stopping after completion');
- // 不做任何操作,保持停止状态
- break;
- case PlayMode.RANDOM: // 3: 随机播放
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Auto-playing random next song');
- await this.playRandomNext();
- break;
- default:
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Unknown play mode: ${playMode}`);
- break;
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error in auto-play handling: ${error}`);
- // 在自动播放失败时,简单记录错误但不触发复杂的错误处理流程
- // 这样可以避免错误处理过程中的潜在阻塞
- this.stateModel.updatePlayingState(false);
- this.stateModel.updateLoadingState(false);
- }
- }
- /**
- * 随机播放下一首歌曲
- */
- private async playRandomNext(): Promise<void> {
- try {
- const playlist = this.playlistModel.getSongs();
- if (playlist.length <= 1) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Not enough songs for random play');
- return;
- }
- const currentIndex = this.playlistModel.getCurrentIndex();
- let randomIndex: number;
- // 确保随机选择的不是当前歌曲
- do {
- randomIndex = Math.floor(Math.random() * playlist.length);
- } while (randomIndex === currentIndex);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Playing random song at index ${randomIndex}`);
- await this.playSongAtIndex(randomIndex);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error in random play: ${error}`);
- // 不再抛出异常,而是通过错误处理机制处理
- await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
- }
- }
- onPlaybackError(what: number, extra: number): void {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Playback error - what: ${what}, extra: ${extra}`);
- this.stateModel.updateLoadingState(false);
- this.stateModel.updatePlayingState(false);
- this.stopProgressTimer();
- // 保存播放状态变化
- this.saveCurrentState();
- // 创建播放器错误对象
- const playbackError = new PlayerError(
- PlayerErrorType.PLAYBACK_ERROR,
- `Playback error: what=${what}, extra=${extra}`,
- what
- );
- playbackError.recoverable = what !== -1004 && what !== -1007; // 某些错误码可恢复
- playbackError.retryCount = 0;
- // 创建错误上下文
- const errorContext: ErrorContext = {
- currentSong: this.getCurrentSong(),
- playlist: this.getPlaylist(),
- currentIndex: this.getCurrentIndex(),
- retryCount: 0,
- isNetworkAvailable: true // 这里可以实际检查网络状态
- };
- // 使用错误恢复策略处理错误
- this.errorRecovery.handleError(playbackError, errorContext).then((action: ErrorRecoveryAction) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error recovery action: ${action}`);
- // 这里可以根据 action 执行相应的恢复操作
- }).catch((recoveryError: Error) => {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error recovery failed: ${recoveryError}`);
- });
- }
- // ========== 卡片更新相关方法 ==========
- /**
- * 播放状态变化时更新卡片
- */
- private async updateWidgetsForPlayStateChange(): Promise<void> {
- try {
- this.updateAllForms();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Widgets updated for play state change`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update widgets for play state change: ${error}`);
- }
- }
- /**
- * 更新所有桌面卡片状态(优化为UI优先响应)
- * 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
- */
- async updateAllForms(forceUpdate: boolean = false) {
- // 优化防抖逻辑:缩短防抖时间,提高响应速度
- const now = Date.now();
- const debounceTime = forceUpdate ? 0 : 100; // 强制更新无防抖,普通更新减少到100ms
- if (!forceUpdate && this.lastFormUpdateTime && (now - this.lastFormUpdateTime < debounceTime)) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updateAllForms: Skipping due to debounce protection (${debounceTime}ms)`);
- return;
- }
-
- // 防抖和重入保护,防止短时间内重复执行更新
- if (this.isUpdatingForms && !forceUpdate) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService updateAllForms: Update already in progress, skipping.');
- return;
- }
-
- this.lastFormUpdateTime = now;
- try {
- this.isUpdatingForms = true;
- const context = this.getContext();
- if (!context) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService updateAllForms: No context available');
- return;
- }
- // 优化:异步获取Form列表,不阻塞UI更新
- const formInfoListPromise = this.getFormInfoListAsync(context);
-
- // 优化:立即获取卡片数据,优先响应UI
- const widgetData = await this.getWidgetFormData();
- if (!widgetData) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService updateAllForms: Failed to get widget data.');
- return;
- }
- const bindingData = formBindingData.createFormBindingData(widgetData);
-
- // 等待Form列表获取完成
- const formInfoList = await formInfoListPromise;
- if (formInfoList.length === 0) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService updateAllForms: No forms found');
- return;
- }
- // 优化:并行更新所有卡片,使用批量更新提高效率
- const updatePromises = formInfoList.map(formInfo => {
- // 每个卡片更新都是异步的,避免阻塞
- return formProvider.updateForm(formInfo.formId, bindingData).catch((error: Error) => {
- LogUtils.getInstance().LOGI(`Failed to update form ${formInfo.formId}: ${error.message}`);
- });
- });
-
- await Promise.all(updatePromises);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: All forms updated for ${formInfoList.length} forms.`);
- // 异步保存数据到数据库,不阻塞UI
- setTimeout(() => {
- const currentSong = this.getCurrentSong();
- if (currentSong) {
- this.saveWidgetDataToDatabase(widgetData, currentSong);
- }
- }, 0);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updateAllForms error: ${error}`);
- } finally {
- this.isUpdatingForms = false;
- }
- }
- /**
- * 异步获取FormInfo列表(优化方法)
- */
- private async getFormInfoListAsync(context: Context): Promise<FormInfo[]> {
- try {
- if (WIDGET_DATA_STORAGE_TYPE === WidgetDataStorageType.DATABASE) {
- return await FormRdbHelper.getInstance(context).queryAllForms();
- } else {
- return await this.getFormInfoListFromPreferences(context);
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Failed to get form info list: ${error}`);
- return [];
- }
- }
- public async getWidgetFormData(loadImage: boolean = true): Promise<widgeData | undefined> {
- this.updatePlaylistStateInModelSync();
- const formData: VideoItem = this.getCurrentSong()!; // VideoItem
- const songState: PlayerState = this.getCurrentState(); // PlayerState
- let imgMap: Record<string, number> = {};
- // 从持久化存储获取所有formId - 使用新的getContext方法
- let context = this.getContext();
- // 如果还是没有context,尝试从AppStorage获取
- if (!context) {
- context = AppStorage.get('context') as common.UIAbilityContext;
- }
- // 1. 准备动态数据 (这部分总是实时的)
- // 构建卡片数据,包含VideoItem和PlayerState信息
- const widgetPlayerState: WidgetPlayerState = {
- isPlaying: songState.isPlaying || false,
- isPaused: songState.isPaused || true,
- isLoading: songState.isLoading || false,
- currentPosition: songState.currentPosition || 0,
- duration: songState.duration || 0,
- hasNext: songState.hasNext || false,
- hasPrevious: songState.hasPrevious || false,
- playMode: songState.playMode || 0
- };
- const widgetPlaylistInfo: WidgetPlaylistInfo = {
- currentIndex: this.getCurrentIndex(),
- totalCount: this.getPlaylist().length
- };
- const widgetTimeInfo: WidgetTimeInfo = {
- currentTimeText: this.formatTimeDisplay(Math.floor((songState.currentPosition || 0) / 1000)),
- totalTimeText: this.formatTimeDisplay(Math.floor((songState.duration || 0) / 1000)),
- progressPercentage: this.calculateProgressPercentage(songState.currentPosition || 0, songState.duration || 0)
- };
- if (!formData) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService getWidgetFormData: No current song, returning default data.');
- const defaultData: widgeData = {
- id: '',
- name: '未知歌曲',
- artist: '未知艺术家',
- album: '未知专辑',
- pixelMapPath: '',
- duration: 0,
- filePath: '',
- imgName: '',
- imageColorHex: '2A2A2A',
- isPlaying: false,
- isPaused: true,
- isLoading: false,
- hasNext: false,
- hasPrevious: false,
- isFavorite: false,
- currentPosition: 0,
- playMode: 0,
- currentIndex: 0,
- totalCount: 0,
- currentTimeText: '',
- totalTimeText: '',
- progressPercentage: 0,
- playerState: widgetPlayerState,
- playlistInfo: widgetPlaylistInfo,
- timeInfo: widgetTimeInfo,
- formImages: imgMap
- }
- return defaultData;
- }
- // 2. 处理静态数据(利用缓存)
- // 如果歌曲已切换,则清空缓存
- if (this.widgetDataCache?.filePath !== formData.filePath) {
- this.widgetDataCache = null;
- }
- if (!this.widgetDataCache) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Cache miss for [${formData.name}], generating static data.`);
- // 缓存未命中,生成并缓存静态数据
- let finalImagePath: string | null = null;
- if (loadImage && formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
- if (formData.pixelMapPath.startsWith('http')) {
- // 网络图片,走缓存/下载逻辑
- if (this.imageCacheDir) {
- const cacheFileName = this.generateCacheFileName(formData.pixelMapPath);
- const cacheFilePath = `${this.imageCacheDir}/${cacheFileName}`;
- if (await this.isCacheValid(cacheFilePath)) {
- finalImagePath = cacheFilePath;
- } else {
- const downloadSuccess = await this.downloadImageToCache(formData.pixelMapPath, cacheFilePath);
- if (downloadSuccess) {
- finalImagePath = cacheFilePath;
- }
- }
- }
- } else {
- // 本地图片
- finalImagePath = formData.pixelMapPath;
- }
- }
- this.widgetDataCache = {
- id: formData.id || '',
- name: formData.name || '',
- artist: formData.artist || '',
- album: formData.album || '',
- pixelMapPath: formData.pixelMapPath || '',
- duration: typeof formData.duration === 'string' ? parseInt(formData.duration || '0') : (formData.duration || 0),
- filePath: formData.filePath || '',
- imageColorHex: '2A2A2A', // 默认颜色,UI侧会根据图片重新提取
- finalImagePath: finalImagePath
- };
- } else {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Cache hit for [${formData.name}].`);
- }
- // 3. 组合静态和动态数据,并处理图片文件描述符
- const cachedData = this.widgetDataCache;
- let imgName: string = '';
- if (cachedData.finalImagePath) {
- try {
- // 优化:复用已缓存的图片文件描述符,避免重复文件操作
- const currentSongPath = cachedData.finalImagePath;
- const cacheKey = `img_${currentSongPath.replace(/[^a-zA-Z0-9]/g, '_')}`;
-
- // 检查是否有缓存的图片名称,避免重复生成
- if (this.lastImageCache && this.lastImageCache.path === currentSongPath && this.lastImageCache.imgName) {
- imgName = this.lastImageCache.imgName;
- const file = fileIo.openSync(currentSongPath, fileIo.OpenMode.READ_ONLY);
- imgMap[imgName] = file.fd;
- } else {
- // 仅在图片真正改变时才重新打开文件
- const file = fileIo.openSync(currentSongPath, fileIo.OpenMode.READ_ONLY);
- imgName = `songCover_${cacheKey}`;
- imgMap[imgName] = file.fd;
-
- // 缓存当前图片信息
- this.lastImageCache = {
- path: currentSongPath,
- imgName: imgName
- } as ImageCacheInfo;
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to open image file [${cachedData.finalImagePath}]: ${JSON.stringify(error)}`);
- imgMap = {};
- imgName = '';
- }
- }
- // 4. 构建最终的完整数据对象
- const widgetData: widgeData = {
- // 从缓存读取静态数据
- id: cachedData.id,
- name: cachedData.name,
- artist: cachedData.artist,
- album: cachedData.album,
- pixelMapPath: cachedData.pixelMapPath,
- duration: cachedData.duration,
- filePath: cachedData.filePath,
- imgName: imgName,
- imageColorHex: cachedData.imageColorHex,
- // PlayerState数据平铺 (实时)
- isPlaying: widgetPlayerState.isPlaying,
- isPaused: widgetPlayerState.isPaused,
- isLoading: widgetPlayerState.isLoading,
- currentPosition: widgetPlayerState.currentPosition,
- hasNext: widgetPlayerState.hasNext,
- hasPrevious: widgetPlayerState.hasPrevious,
- playMode: widgetPlayerState.playMode,
- // 播放列表信息平铺 (实时)
- currentIndex: widgetPlaylistInfo.currentIndex,
- totalCount: widgetPlaylistInfo.totalCount,
- // 时间信息平铺 (实时)
- currentTimeText: widgetTimeInfo.currentTimeText,
- totalTimeText: widgetTimeInfo.totalTimeText,
- progressPercentage: widgetTimeInfo.progressPercentage,
- isFavorite: this.getCurrentSongFavoriteState(), // 收藏状态 (实时)
- // 保持嵌套结构兼容性
- playerState: widgetPlayerState,
- playlistInfo: widgetPlaylistInfo,
- timeInfo: widgetTimeInfo,
- // 卡片图片显示必填字段
- formImages: imgMap,
- };
- // 将widgetData存入数据库
- this.saveWidgetDataToDatabase(widgetData, formData);
- return widgetData;
- }
- /**
- * 计算播放进度百分比
- */
- private calculateProgressPercentage(current: number, total: number): number {
- if (total <= 0) {
- return 0;
- }
- return Math.min(100, Math.max(0, (current / total) * 100));
- }
- /**
- * 格式化时间显示
- */
- private formatTimeDisplay(seconds: number): string {
- const mins = Math.floor(seconds / 60);
- const secs = Math.floor(seconds % 60);
- return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
- }
- /**
- * 将widgetData存入存储中
- * 根据配置选择数据库或Preferences存储
- */
- private async saveWidgetDataToDatabase(widgetData: widgeData, formData: VideoItem): Promise<void> {
- try {
- // 从getContext方法获取context
- let context = this.getContext();
-
- // 如果还是没有context,尝试从AppStorage获取
- if (!context) {
- context = AppStorage.get('context') as common.UIAbilityContext;
- }
- if (!context) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService saveWidgetDataToDatabase: No context available');
- return;
- }
- if (!formData || !formData.id) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService saveWidgetDataToDatabase: No valid current song');
- return;
- }
- const widgetDataInfo = new WidgetDataInfo();
-
- // 基本信息
- widgetDataInfo.songId = formData.id;
- widgetDataInfo.name = formData.name || '';
- widgetDataInfo.artist = formData.artist || '';
- widgetDataInfo.album = formData.album || '';
- widgetDataInfo.pixelMapPath = formData.pixelMapPath || '';
- widgetDataInfo.duration = typeof formData.duration === 'string' ? parseInt(formData.duration || '0') : (formData.duration || 0);
- widgetDataInfo.filePath = formData.filePath || '';
- widgetDataInfo.imgName = widgetData.imgName || '';
- widgetDataInfo.imageColorHex = widgetData.imageColorHex || '2A2A2A';
- widgetDataInfo.isFavorite = widgetData.isFavorite || false;
- // 播放状态信息
- widgetDataInfo.isPlaying = widgetData.isPlaying;
- widgetDataInfo.isPaused = widgetData.isPaused;
- widgetDataInfo.isLoading = widgetData.isLoading;
- widgetDataInfo.currentPosition = widgetData.currentPosition;
- widgetDataInfo.hasNext = widgetData.hasNext;
- widgetDataInfo.hasPrevious = widgetData.hasPrevious;
- widgetDataInfo.playMode = widgetData.playMode;
- // 播放列表信息
- widgetDataInfo.currentIndex = widgetData.currentIndex;
- widgetDataInfo.totalCount = widgetData.totalCount;
- // 时间信息
- widgetDataInfo.currentTimeText = widgetData.currentTimeText;
- widgetDataInfo.totalTimeText = widgetData.totalTimeText;
- widgetDataInfo.progressPercentage = widgetData.progressPercentage;
- // 根据配置选择存储方式
- if (WIDGET_DATA_STORAGE_TYPE === WidgetDataStorageType.DATABASE) {
- await WidgetDataRdbHelper.getInstance(context).insertOrUpdateWidgetData(widgetDataInfo);
- LogUtils.getInstance().LOGI(`UnifiedPlayerService saveWidgetDataToDatabase: Widget data saved to database for song: ${formData.name}`);
- } else {
- await this.saveWidgetDataToPreferences(widgetDataInfo, context);
- LogUtils.getInstance().LOGI(`UnifiedPlayerService saveWidgetDataToDatabase: Widget data saved to preferences for song: ${formData.name}`);
- }
-
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService saveWidgetDataToDatabase: Failed to save widget data to storage: ${error}`);
- }
- }
- /**
- * 将widgetData保存到Preferences
- */
- private async saveWidgetDataToPreferences(widgetDataInfo: WidgetDataInfo, context: Context): Promise<void> {
- try {
- const preferencesUtil = PreferencesUtil.getInstance();
- const prefs = await preferencesUtil.getPreferences(context);
-
- // 将WidgetDataInfo转换为IWidgetData接口格式
- const widgetData: IWidgetData = {
- songId: widgetDataInfo.songId,
- name: widgetDataInfo.name,
- artist: widgetDataInfo.artist,
- album: widgetDataInfo.album,
- pixelMapPath: widgetDataInfo.pixelMapPath,
- duration: widgetDataInfo.duration,
- filePath: widgetDataInfo.filePath,
- imgName: widgetDataInfo.imgName,
- imageColorHex: widgetDataInfo.imageColorHex,
- isFavorite: widgetDataInfo.isFavorite,
- isPlaying: widgetDataInfo.isPlaying,
- isPaused: widgetDataInfo.isPaused,
- isLoading: widgetDataInfo.isLoading,
- currentPosition: widgetDataInfo.currentPosition,
- hasNext: widgetDataInfo.hasNext,
- hasPrevious: widgetDataInfo.hasPrevious,
- playMode: widgetDataInfo.playMode,
- currentIndex: widgetDataInfo.currentIndex,
- totalCount: widgetDataInfo.totalCount,
- currentTimeText: widgetDataInfo.currentTimeText,
- totalTimeText: widgetDataInfo.totalTimeText,
- progressPercentage: widgetDataInfo.progressPercentage,
- createTime: widgetDataInfo.createTime,
- updateTime: widgetDataInfo.updateTime
- };
-
- await preferencesUtil.saveWidgetData(prefs, widgetData);
- LogUtils.getInstance().LOGI(`UnifiedPlayerService saveWidgetDataToPreferences: Widget data saved to preferences for song: ${widgetDataInfo.name}`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService saveWidgetDataToPreferences: Failed to save widget data to preferences: ${error}`);
- }
- }
- /**
- * 从Preferences获取FormInfo列表
- */
- private async getFormInfoListFromPreferences(context: Context): Promise<FormInfo[]> {
- try {
- const preferencesUtil = PreferencesUtil.getInstance();
- const prefs = await preferencesUtil.getPreferences(context);
-
- const formInfoDataList = await preferencesUtil.getAllFormInfos(prefs);
-
- // 将IFormInfo转换为FormInfo类型
- const formInfoList: FormInfo[] = formInfoDataList.map(data => {
- const formInfo = new FormInfo();
- formInfo.formId = data.formId;
- formInfo.formName = data.formName;
- formInfo.formDimension = data.formDimension;
- formInfo.createTime = data.createTime;
- formInfo.updateTime = data.updateTime;
- return formInfo;
- });
-
- LogUtils.getInstance().LOGI(`UnifiedPlayerService getFormInfoListFromPreferences: Found ${formInfoList.length} forms in preferences`);
- LogUtils.getInstance().LOGI(`UnifiedPlayerService getFormInfoListFromPreferences: Using context: ${context ? 'Available' : 'NULL'}, Process: ${AppStorage.get('processName') || 'Unknown'}`);
- return formInfoList;
- } catch (error) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService getFormInfoListFromPreferences: Failed to get forms from preferences: ${error}`);
- return [];
- }
- }
- /**
- * 状态变化时更新卡片
- */
- public async updateWidgetsForStateChange(): Promise<void> {
- try {
- await this.updateAllForms(true); // 强制更新,因为状态确实发生了变化
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Widgets updated for state change`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update widgets for state change: ${error}`);
- }
- }
- /**
- * 播放列表变化时更新卡片
- */
- private async updateWidgetsForPlaylistChange(): Promise<void> {
- try {
- this.updateAllForms();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Widgets updated for playlist change`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update widgets for playlist change: ${error}`);
- }
- }
- /**
- * 应用启动时初始化卡片显示
- */
- public async initializeWidgetDisplay(): Promise<void> {
- try {
- this.updateAllForms();
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Widget display initialized');
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to initialize widget display: ${error}`);
- }
- }
- /**
- * 解析时长字符串为毫秒数
- */
- private parseDuration(durationStr: string): number {
- if (!durationStr || durationStr === '0') {
- return 0;
- }
- try {
- // 如果是纯数字,假设是秒数
- const numValue = parseFloat(durationStr);
- if (!isNaN(numValue)) {
- const result = Math.floor(numValue * 1000); // 转换为毫秒
- return result;
- }
- // 如果包含冒号,解析为 MM:SS 或 HH:MM:SS 格式
- if (durationStr.includes(':')) {
- const parts = durationStr.split(':').map(part => parseInt(part, 10));
- let totalSeconds = 0;
- if (parts.length === 2) {
- // MM:SS 格式
- totalSeconds = parts[0] * 60 + parts[1];
- } else if (parts.length === 3) {
- // HH:MM:SS 格式
- totalSeconds = parts[0] * 3600 + parts[1] * 60 + parts[2];
- }
- const result = totalSeconds * 1000; // 转换为毫秒
- return result;
- }
- return 0;
- } catch (error) {
- return 0;
- }
- }
- // ==================== PlayerStateCallback 实现 ====================
- /**
- * 转换播放模式为AVSession循环模式
- */
- private convertPlayModeToLoopMode(playMode: number): avSession.LoopMode {
- switch (playMode) {
- case PlayMode.SINGLE_REPEAT:
- return avSession.LoopMode.LOOP_MODE_SINGLE;
- case PlayMode.NORMAL:
- return avSession.LoopMode.LOOP_MODE_LIST;
- case PlayMode.RANDOM:
- return avSession.LoopMode.LOOP_MODE_SHUFFLE;
- case PlayMode.SEQUENCE:
- default:
- return avSession.LoopMode.LOOP_MODE_SEQUENCE;
- }
- }
- /**
- * 更新AVSession播放状态(带防抖)
- */
- private updateSessionPlayState(force: boolean = false): void {
- // 清除之前的定时器
- if (this.avSessionUpdateTimer !== -1) {
- clearTimeout(this.avSessionUpdateTimer);
- }
- if (force) {
- // 强制更新,立即执行,绕过频率限制
- this.doUpdateSessionPlayState(true);
- } else {
- // 增加防抖延迟到500ms,减少频繁更新
- this.avSessionUpdateTimer = setTimeout(() => {
- this.doUpdateSessionPlayState(false);
- }, 500);
- }
- }
- /**
- * 实际执行AVSession播放状态更新
- * @param force 是否强制更新,绕过频率限制
- */
- private doUpdateSessionPlayState(force: boolean = false): void {
- try {
- if (!this.avSessionController) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession controller not initialized');
- return;
- }
- // 避免过于频繁的更新(最小间隔300ms,保证关键操作响应性)
- // 强制更新时绕过频率限制
- const now = Date.now();
- if (!force && now - this.lastAvSessionUpdate < 300) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Skipping AVSession update due to rate limit');
- return;
- }
- // 优化:确保使用最新的歌曲数据,避免状态不一致
- const currentSong = this.playlistModel.getCurrentSong();
- const currentPosition = this.getCurrentPosition();
-
- // 添加数据一致性检查
- if (!currentSong) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No current song available for AVSession update');
- return;
- }
- // 优先从播放器获取实际duration
- let duration = 0;
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (ijkPlayer) {
- duration = ijkPlayer.getDuration();
- }
- if (duration <= 0 && currentSong?.duration) {
- duration = this.parseDuration(currentSong.duration);
- }
- const currentState = this.stateModel.getState();
- // 按照官方文档要求设置完整的播放状态
- const playbackState: avSession.AVPlaybackState = {
- // 播放状态 - 修复:确保播放状态正确反映当前状态
- state: currentState.isPlaying ? avSession.PlaybackState.PLAYBACK_STATE_PLAY :
- avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
- // 播放速度
- speed: currentState.speed || 1.0,
- // 循环模式
- loopMode: this.convertPlayModeToLoopMode(currentState.playMode),
- // 收藏状态
- isFavorite: this.getCurrentSongFavoriteState()
- };
- // 只有当duration > 0时才设置位置信息,避免进度条显示问题
- if (duration > 0) {
- playbackState.position = {
- elapsedTime: Math.max(0, currentPosition), // 已播放时间(毫秒)
- updateTime: Date.now() // 更新时间戳
- };
- playbackState.bufferedTime = Math.max(currentPosition, 0);
- } else {
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Skipping position info due to invalid duration: ${duration}ms`);
- }
- this.avSessionController.setAvSessionPlayState(playbackState);
- // 记录上次更新时间
- this.lastAvSessionUpdate = now;
-
- // 添加调试日志,便于问题排查
- const updateType = force ? "FORCED" : "NORMAL";
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: AVSession state updated (${updateType}) - isPlaying: ${currentState.isPlaying}, position: ${Math.max(0, currentPosition)}ms, duration: ${duration}ms`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update AVSession play state: ${error}`);
- }
- }
- /**
- * 更新AVSession元数据(优化防抖逻辑,修复竞态条件)
- */
- private async updateAvSessionMetadata(song: VideoItem): Promise<void> {
- // 清除之前的定时器,避免重复更新
- if (this.avMetadataUpdateTimer !== -1) {
- clearTimeout(this.avMetadataUpdateTimer);
- this.avMetadataUpdateTimer = -1;
- }
-
- // 优化:减少防抖时间,提高响应速度
- const now = Date.now();
- if (now - this.lastAvSessionUpdateTime < 500) {
- // 如果更新过于频繁,延迟到下一个时间窗口
- this.avMetadataUpdateTimer = setTimeout(() => {
- this.doUpdateAvSessionMetadata(song);
- }, 500 - (now - this.lastAvSessionUpdateTime));
- return;
- }
-
- this.lastAvSessionUpdateTime = now;
-
- // 检查播放器状态,决定更新时机
- const ijkPlayer = this.playerManager.getIjkPlayer();
- const playerDuration = ijkPlayer ? ijkPlayer.getDuration() : 0;
- const shouldWaitForPlayer = playerDuration <= 0 && this.isPlayerPreparedForCurrentSong === false;
-
- if (shouldWaitForPlayer) {
- // 播放器还没准备好,适当延迟
- this.avMetadataUpdateTimer = setTimeout(() => {
- this.doUpdateAvSessionMetadata(song);
- }, 300);
- } else {
- // 播放器已准备好,立即更新
- this.avMetadataUpdateTimer = setTimeout(() => {
- this.doUpdateAvSessionMetadata(song);
- }, 50);
- }
- }
- /**
- * 实际执行AVSession元数据更新
- */
- private async doUpdateAvSessionMetadata(song: VideoItem): Promise<void> {
- try {
- if (!this.avSessionController) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: AVSession controller not initialized');
- return;
- }
- // 关键修复:验证歌曲数据的一致性,确保使用最新的歌曲信息
- const currentSong = this.playlistModel.getCurrentSong();
- if (!currentSong || currentSong.filePath !== song.filePath) {
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Song data inconsistency detected. Current: ${currentSong?.name}, Provided: ${song.name}`);
- // 使用最新的歌曲数据
- if (currentSong) {
- song = currentSong;
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using latest song data: ${song.name}`);
- } else {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No current song available, skipping metadata update');
- return;
- }
- }
- // 减少防抖时间,提高响应速度
- const now = Date.now();
- if (now - this.lastAvMetadataUpdate < 300) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: Skipping AVSession metadata update due to rate limit');
- return;
- }
- // 调试:打印VideoItem的duration字段
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: VideoItem duration field - value: '${song.duration}', type: ${typeof song.duration}`);
- // 智能获取最准确的duration
- let duration = 0;
- try {
- const ijkPlayer = this.playerManager.getIjkPlayer();
- if (ijkPlayer && this.isPlayerPreparedForCurrentSong) {
- const playerDuration = ijkPlayer.getDuration();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Player getDuration() returned: ${playerDuration}ms`);
- if (playerDuration > 0) {
- duration = playerDuration;
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Using player duration: ${duration}ms`);
- } else {
- // 备用方案:解析VideoItem中的duration
- duration = song.duration ? this.parseDuration(song.duration) : 0;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Player duration invalid, using parsed duration: ${duration}ms from '${song.duration}'`);
- }
- } else {
- duration = song.duration ? this.parseDuration(song.duration) : 0;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Player not ready, using parsed duration: ${duration}ms from '${song.duration}'`);
- }
- } catch (error) {
- duration = song.duration ? this.parseDuration(song.duration) : 0;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Error getting player duration, using parsed: ${duration}ms from '${song.duration}', error: ${error}`);
- }
- // 最终验证duration
- if (isNaN(duration) || duration <= 0) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Final duration is invalid (${duration}), setting to 0`);
- duration = 0;
- }
- // 获取歌词内容(如果有的话)
- const lyricContent = song.lyricContent || '';
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: About to call setAVMetadataMusic with duration: ${duration}ms (type: ${typeof duration})`);
- await this.avSessionController.setAVMetadataMusic(song, duration, lyricContent);
- // 记录更新时间
- this.lastAvMetadataUpdate = now;
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: AVSession metadata updated for ${song.name} with duration ${duration}ms`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update AVSession metadata: ${error}`);
- }
- }
- // ==================== 辅助方法 ====================
- /**
- * 初始化图片缓存目录
- */
- private async initImageCacheDirectory(): Promise<void> {
- try {
- if (this.context) {
- const cacheDir = this.context.getApplicationContext().cacheDir;
- this.imageCacheDir = cacheDir + '/cover_images';
- // 创建缓存目录
- if (!fileIo.accessSync(this.imageCacheDir)) {
- fileIo.mkdirSync(this.imageCacheDir);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Created image cache directory: ${this.imageCacheDir}`);
- }
- // 清理过期缓存
- await this.cleanExpiredCache();
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to initialize image cache directory: ${error}`);
- }
- }
- /**
- * 生成图片URL的哈希值作为缓存文件名
- */
- private generateCacheFileName(url: string): string {
- try {
- const textEncoder = new util.TextEncoder();
- const data = textEncoder.encodeInto(url);
- // 使用URL的简单哈希算法
- let hash = 0;
- for (let i = 0; i < data.byteLength; i++) {
- const char = data[i];
- hash = ((hash << 5) - hash) + char;
- hash = hash & hash; // 转换为32位整数
- }
- return Math.abs(hash).toString(16) + '.jpg';
- } catch (error) {
- // 如果哈希生成失败,使用URL的简单替换方案
- return url.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 50) + '.jpg';
- }
- }
- /**
- * 检查缓存文件是否存在且未过期
- */
- private async isCacheValid(cacheFilePath: string): Promise<boolean> {
- try {
- if (!fileIo.accessSync(cacheFilePath)) {
- return false;
- }
- const stat = fileIo.statSync(cacheFilePath);
- const fileAge = Date.now() - stat.mtime;
- const maxAge = this.CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000; // 转换为毫秒
- return fileAge < maxAge;
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to check cache validity: ${error}`);
- return false;
- }
- }
- /**
- * 下载网络图片到缓存
- */
- private async downloadImageToCache(url: string, cacheFilePath: string): Promise<boolean> {
- try {
- const httpRequest = http.createHttp();
- try {
- const response = await httpRequest.request(url, {
- method: http.RequestMethod.GET,
- connectTimeout: 10000, // 10秒超时
- readTimeout: 10000
- });
- if (response.responseCode === http.ResponseCode.OK && response.result) {
- const tempFile = fileIo.openSync(cacheFilePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
- try {
- await fileIo.write(tempFile.fd, response.result as ArrayBuffer);
- fileIo.closeSync(tempFile.fd);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Downloaded image to cache: ${cacheFilePath}`);
- return true;
- } catch (writeError) {
- fileIo.closeSync(tempFile.fd);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Write error: ${writeError}`);
- return false;
- }
- } else {
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Failed to download image ${url}: HTTP ${response.responseCode}`);
- return false;
- }
- } finally {
- httpRequest.destroy();
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Error downloading image to cache: ${error}`);
- return false;
- }
- }
- /**
- * 清理过期缓存
- */
- private async cleanExpiredCache(): Promise<void> {
- try {
- if (!this.imageCacheDir || !fileIo.accessSync(this.imageCacheDir)) {
- return;
- }
- const files = fileIo.listFileSync(this.imageCacheDir);
- const maxAge = this.CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000;
- let totalSize = 0;
- const fileInfos: Array<CacheFileInfo> = [];
- // 收集文件信息
- for (const file of files) {
- const filePath = this.imageCacheDir + '/' + file;
- try {
- const stat = fileIo.statSync(filePath);
- totalSize += stat.size;
- const fileInfo: CacheFileInfo = {
- path: filePath,
- size: stat.size,
- mtime: stat.mtime
- };
- fileInfos.push(fileInfo);
- } catch (error) {
- // 忽略无法读取的文件
- }
- }
- // 删除过期文件
- const now = Date.now();
- for (const fileInfo of fileInfos) {
- if (now - fileInfo.mtime > maxAge) {
- try {
- fileIo.unlinkSync(fileInfo.path);
- totalSize -= fileInfo.size;
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Deleted expired cache file: ${fileInfo.path}`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to delete expired cache file: ${error}`);
- }
- }
- }
- // 如果缓存大小超限,删除最旧的文件
- const maxSizeBytes = this.MAX_CACHE_SIZE_MB * 1024 * 1024;
- if (totalSize > maxSizeBytes) {
- const sortedFiles = fileInfos
- .filter(f => fileIo.accessSync(f.path))// 只保留仍存在的文件
- .sort((a, b) => a.mtime - b.mtime); // 按修改时间排序,最旧的在前
- for (const fileInfo of sortedFiles) {
- if (totalSize <= maxSizeBytes) {
- break;
- }
- try {
- fileIo.unlinkSync(fileInfo.path);
- totalSize -= fileInfo.size;
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Deleted cache file due to size limit: ${fileInfo.path}`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to delete cache file: ${error}`);
- }
- }
- }
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService: Cache cleanup completed, total size: ${(totalSize / 1024 / 1024).toFixed(2)}MB`);
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to clean expired cache: ${error}`);
- }
- }
- /**
- * 清理无效的Form ID
- * 应用启动时调用,验证所有保存的Form ID是否仍然有效
- */
- private async cleanupInvalidFormIds(): Promise<void> {
- try {
- if (!this.context) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No context for cleanup invalid form IDs');
- return;
- }
- // 根据存储配置读取所有Form信息
- let formInfoList: FormInfo[] = [];
- try {
- if (WIDGET_DATA_STORAGE_TYPE === WidgetDataStorageType.DATABASE) {
- formInfoList = await FormRdbHelper.getInstance(this.context).queryAllForms();
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Found ${formInfoList.length} forms in database`);
- } else {
- formInfoList = await this.getFormInfoListFromPreferences(this.context);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Found ${formInfoList.length} forms in preferences`);
- }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Failed to query forms: ${error}`);
- return;
- }
- if (formInfoList.length === 0) {
- LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService cleanupInvalidFormIds: No forms found');
- return;
- }
- // 记录更新统计
- const invalidFormIds: string[] = [];
- let flatWidgetData = await this.getWidgetFormData();
- // 逐个更新所有卡片,根据卡片类型进行个性化处理
- for (const formInfo of formInfoList) {
- try {
- const formBindingDataInstance = formBindingData.createFormBindingData(flatWidgetData);
- await formProvider.updateForm(formInfo.formId, formBindingDataInstance);
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Successfully updated form ${formInfo.formId} (${formInfo.formName})`);
- } catch (error) {
- const errorStr: string = error?.toString() || '';
- invalidFormIds.push(formInfo.formId);
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Form ${formInfo.formId} (${formInfo.formName}) - ${errorStr}}`);
- }
- }
- // 自动清理无效的Form ID
- if (invalidFormIds.length > 0) {
- try {
- for (const invalidFormId of invalidFormIds) {
- await FormRdbHelper.getInstance(this.context).deleteForm(invalidFormId);
- }
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Removed ${invalidFormIds.length} invalid form IDs from database: ${invalidFormIds.join(', ')}`);
- } catch (error) {
- LogUtils.getInstance()
- .LOGI(`Heanup UnifiedPlayerService cleanupInvalidFormIds: Failed to clean invalid form IDs from database: ${error}`);
- }
- }
- // const preferencesUtil = PreferencesUtil.getInstance();
- // const prefs = await preferencesUtil.getPreferences(this.context);
- // const formIds = await preferencesUtil.getFormIds(prefs);
- //
- // if (formIds.length === 0) {
- // LogUtils.getInstance().LOGI('Heanup UnifiedPlayerService: No form IDs to cleanup');
- // return;
- // }
- //
- // const invalidFormIds: string[] = [];
- //
- // // 逐个验证Form ID的有效性
- // for (const formId of formIds) {
- // try {
- // // 尝试更新卡片来验证其有效性
- // const flatWidgetData = await this.getWidgetFormData();
- // if (flatWidgetData) {
- // const formBindingDataInstance = formBindingData.createFormBindingData(flatWidgetData);
- // await formProvider.updateForm(formId, formBindingDataInstance);
- // }
- // } catch (error) {
- // // 如果更新失败,说明这个Form ID无效
- // invalidFormIds.push(formId);
- // LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Invalid form ID detected: ${formId}, error: ${error}`);
- // }
- // }
- //
- // // 移除无效的Form ID
- // if (invalidFormIds.length > 0) {
- //
- //
- // LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Cleaned up ${invalidFormIds.length} invalid form IDs: ${invalidFormIds.join(', ')}`);
- // } else {
- // LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: All ${formIds.length} form IDs are valid`);
- // }
- } catch (error) {
- LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to cleanup invalid form IDs: ${error}`);
- }
- }
- }
|