1- import { resolveBufferPolicy } from "./buffer-policy" ;
21import { EventEmitter } from "./event-emitter" ;
3- import { HttpClient } from "./http-client" ;
4- import { MediaElementObserver } from "./media-element-observer" ;
5- import { MediaSourceController } from "./media-source-controller" ;
6- import { PlaybackClient } from "./playback-client" ;
7- import { PlaybackLoop } from "./playback-loop" ;
2+ import { createPlayerDeps , type PlayerDeps } from "./player-deps" ;
83import { bufferedEndMs , createSnapshot , type TypeTypeMseSnapshot } from "./player-snapshot" ;
94import { SeekController } from "./seek-controller" ;
10- import { SegmentScheduler } from "./segment-scheduler" ;
115import { type LoadedSession , loadPlaybackSession } from "./session-loader" ;
126import type {
137 TypeTypeMseConfig ,
148 TypeTypeMseEventType ,
159 TypeTypeMseListener ,
10+ TypeTypeMseQuality ,
1611 TypeTypeMseState ,
1712} from "./types" ;
1813
1914export class TypeTypeMsePlayer {
2015 private readonly emitter = new EventEmitter ( ) ;
21- private readonly http : HttpClient ;
22- private readonly playback : PlaybackClient ;
23- private readonly mediaEvents : MediaElementObserver ;
24- private readonly media : MediaSourceController ;
25- private readonly scheduler : SegmentScheduler ;
26- private readonly loop : PlaybackLoop ;
16+ private readonly deps : PlayerDeps ;
2717 private readonly seekController = new SeekController ( ) ;
2818 private session : LoadedSession | null = null ;
2919 private operation = new AbortController ( ) ;
@@ -35,32 +25,16 @@ export class TypeTypeMsePlayer {
3525 private readonly video : HTMLVideoElement ,
3626 private readonly config : TypeTypeMseConfig ,
3727 ) {
38- this . http = new HttpClient (
39- config . headers
40- ? { endpoint : config . endpoint , headers : config . headers }
41- : { endpoint : config . endpoint } ,
42- ) ;
43- this . playback = new PlaybackClient ( this . http ) ;
44- this . mediaEvents = new MediaElementObserver ( {
28+ this . deps = createPlayerDeps ( {
4529 video,
46- state : ( state ) => this . setState ( state ) ,
47- error : ( error ) => this . handleError ( error ) ,
48- } ) ;
49- this . mediaEvents . start ( ) ;
50- this . media = new MediaSourceController ( video ) ;
51- this . scheduler = new SegmentScheduler ( this . http , this . media , this . emitter ) ;
52- this . loop = new PlaybackLoop ( {
53- video,
54- http : this . http ,
55- media : this . media ,
56- scheduler : this . scheduler ,
30+ config,
5731 emitter : this . emitter ,
58- policy : resolveBufferPolicy ( config ) ,
5932 session : ( ) => this . session ,
6033 signal : ( ) => this . operation . signal ,
61- bufferedEndMs : ( ) => bufferedEndMs ( this . video ) ,
34+ state : ( state ) => this . setState ( state ) ,
6235 error : ( error ) => this . handleError ( error ) ,
6336 } ) ;
37+ this . deps . mediaEvents . start ( ) ;
6438 }
6539
6640 on ( type : TypeTypeMseEventType , listener : TypeTypeMseListener ) : ( ) => void {
@@ -73,7 +47,7 @@ export class TypeTypeMsePlayer {
7347 const signal = this . operation . signal ;
7448 this . setState ( "loading" ) ;
7549 const startTimeMs = Math . max ( 0 , Math . round ( this . config . startTimeMs ?? 0 ) ) ;
76- const response = await this . playback . create (
50+ const response = await this . deps . playback . create (
7751 {
7852 videoId : this . config . videoId ,
7953 videoItag : this . config . videoItag ,
@@ -102,6 +76,13 @@ export class TypeTypeMsePlayer {
10276 return this . seekController . seek ( positionMs , ( targetMs ) => this . performSeek ( targetMs ) ) ;
10377 }
10478
79+ async setQuality ( quality : TypeTypeMseQuality ) : Promise < void > {
80+ this . operation . abort ( ) ;
81+ return this . seekController . seek ( this . currentTimeMs ( ) , ( targetMs ) =>
82+ this . performSeek ( targetMs , quality ) ,
83+ ) ;
84+ }
85+
10586 snapshot ( ) : TypeTypeMseSnapshot {
10687 return createSnapshot ( this . video , this . state , this . session , bufferedEndMs ( this . video ) ) ;
10788 }
@@ -111,25 +92,37 @@ export class TypeTypeMsePlayer {
11192 this . destroyed = true ;
11293 this . operation . abort ( ) ;
11394 this . seekController . reset ( ) ;
114- this . loop . stop ( ) ;
115- this . mediaEvents . stop ( ) ;
116- this . media . detach ( ) ;
95+ this . deps . loop . stop ( ) ;
96+ this . deps . mediaEvents . stop ( ) ;
97+ this . deps . media . detach ( ) ;
11798 this . emitter . clear ( ) ;
11899 this . state = "destroyed" ;
119100 }
120101
121- private async performSeek ( positionMs : number ) : Promise < void > {
102+ private async performSeek ( positionMs : number , quality ?: TypeTypeMseQuality ) : Promise < void > {
122103 this . ensureAlive ( ) ;
123104 const current = this . session ;
124105 if ( ! current ) throw new Error ( "Player is not loaded" ) ;
125106 const revision = this . nextRevision ( ) ;
126107 const signal = this . operation . signal ;
127108 const targetMs = Math . max ( 0 , Math . round ( positionMs ) ) ;
128- this . loop . stop ( ) ;
109+ this . deps . loop . stop ( ) ;
129110 this . setState ( "seeking" ) ;
130111 this . emitter . emit ( { type : "seek" , positionMs : targetMs } ) ;
131- const response = await this . playback . seek ( current . response . sessionId , targetMs , signal ) ;
112+ const response = await this . deps . playback . seek (
113+ current . response . sessionId ,
114+ targetMs ,
115+ quality ,
116+ signal ,
117+ ) ;
132118 await this . switchSession ( response , targetMs , revision , signal ) ;
119+ if ( quality ) {
120+ this . emitter . emit ( {
121+ type : "quality" ,
122+ videoItag : quality . videoItag ,
123+ audioItag : quality . audioItag ?? null ,
124+ } ) ;
125+ }
133126 }
134127
135128 private async switchSession (
@@ -139,19 +132,19 @@ export class TypeTypeMsePlayer {
139132 signal : AbortSignal ,
140133 ) : Promise < void > {
141134 const session = await loadPlaybackSession ( {
142- http : this . http ,
143- playback : this . playback ,
144- media : this . media ,
145- scheduler : this . scheduler ,
135+ http : this . deps . http ,
136+ playback : this . deps . playback ,
137+ media : this . deps . media ,
138+ scheduler : this . deps . scheduler ,
146139 video : this . video ,
147140 response,
148141 startTimeMs,
149142 signal,
150143 } ) ;
151144 if ( ! this . isCurrent ( revision ) ) return ;
152145 this . session = session ;
153- await this . loop . fillOnce ( ) ;
154- this . loop . start ( ) ;
146+ await this . deps . loop . fillOnce ( ) ;
147+ this . deps . loop . start ( ) ;
155148 this . emitter . emit ( {
156149 type : "manifest" ,
157150 generation : response . generation ,
@@ -176,6 +169,10 @@ export class TypeTypeMsePlayer {
176169 if ( this . destroyed ) throw new Error ( "Player is destroyed" ) ;
177170 }
178171
172+ private currentTimeMs ( ) : number {
173+ return Math . max ( 0 , Math . round ( this . video . currentTime * 1000 ) ) ;
174+ }
175+
179176 private nextRevision ( ) : number {
180177 this . operation . abort ( ) ;
181178 this . operation = new AbortController ( ) ;
0 commit comments