Skip to content

Commit 5aae99b

Browse files
committed
chore: add types, and device name in audiostate
1 parent 4c72b8e commit 5aae99b

4 files changed

Lines changed: 273 additions & 64 deletions

File tree

src/adapters/cast.ts

Lines changed: 81 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import type { JoinPolicy, MediaTrack } from 'types';
1212
*/
1313
class CastAdapter {
1414
private static _instance: CastAdapter | null = null;
15-
private castContext: any | null = null;
16-
private chromeCast: any | null = null;
17-
private remotePlayer: any | null = null;
18-
private remotePlayerController: any | null = null;
15+
private castContext: cast.framework.CastContext | null = null;
16+
private chromeCast: typeof chrome.cast | null = null;
17+
private remotePlayer: cast.framework.RemotePlayer | null = null;
18+
private remotePlayerController: cast.framework.RemotePlayerController | null = null;
1919
private isInitialized = false;
2020
private castButtonId = 'cast-button-container';
2121
private sessionStateListener: ((event: any) => void) | null = null;
@@ -39,7 +39,11 @@ class CastAdapter {
3939
* @param retryDelay - Delay between retries in milliseconds
4040
* @returns Promise resolving to the cast context if successful
4141
*/
42-
async load(retryCount = 0, maxRetries = 15, retryDelay = 500): Promise<any | null> {
42+
async load(
43+
retryCount = 0,
44+
maxRetries = 15,
45+
retryDelay = 500,
46+
): Promise<cast.framework.CastContext | null> {
4347
try {
4448
// Check if Cast API is already available
4549
if (typeof window !== 'undefined' && window.cast && window.cast.framework) {
@@ -108,7 +112,7 @@ class CastAdapter {
108112
async init(
109113
receiverId: string = DEFAULT_CAST_CONFIG.receiverId,
110114
joinPolicy: keyof JoinPolicy = DEFAULT_CAST_CONFIG.joinPolicy,
111-
): Promise<any | undefined> {
115+
): Promise<cast.framework.CastContext | undefined> {
112116
try {
113117
if (this.isInitialized) {
114118
console.warn('Cast framework is already initialized.');
@@ -162,12 +166,12 @@ class CastAdapter {
162166
* Get the Cast context, initializing it if necessary.
163167
* @returns The Cast context or null if not available
164168
*/
165-
private getCastContext(): any | null {
169+
private getCastContext(): cast.framework.CastContext | null {
166170
if (!this.castContext && typeof window !== 'undefined') {
167171
try {
168172
if (isValidWindow && window.cast?.framework) {
169173
this.castContext = window.cast.framework.CastContext.getInstance();
170-
this.chromeCast = window.chrome.cast;
174+
this.chromeCast = window.chrome?.cast ?? null;
171175
return this.castContext;
172176
}
173177
console.warn('Cast framework not loaded yet');
@@ -245,16 +249,18 @@ class CastAdapter {
245249
return;
246250
}
247251

248-
switch (event.sessionState) {
249-
case window.cast.framework.SessionState.SESSION_STARTED:
250-
this.onCastSessionStarted();
251-
break;
252-
case window.cast.framework.SessionState.SESSION_RESUMED:
253-
this.onCastSessionResumed();
254-
break;
255-
case window.cast.framework.SessionState.SESSION_ENDED:
256-
this.onCastSessionEnded();
257-
break;
252+
if (window.cast?.framework) {
253+
switch (event.sessionState) {
254+
case window.cast.framework.SessionState.SESSION_STARTED:
255+
this.onCastSessionStarted();
256+
break;
257+
case window.cast.framework.SessionState.SESSION_RESUMED:
258+
this.onCastSessionResumed();
259+
break;
260+
case window.cast.framework.SessionState.SESSION_ENDED:
261+
this.onCastSessionEnded();
262+
break;
263+
}
258264
}
259265
};
260266

@@ -274,10 +280,20 @@ class CastAdapter {
274280
if (session) {
275281
this.setupRemotePlayer();
276282

277-
// Notify state change
283+
// Get the receiver name (cast device name)
284+
const castDevice = session.getCastDevice();
285+
const deviceName = castDevice?.friendlyName || 'Cast Device';
286+
287+
// Notify state change with device name
278288
ChangeNotifier.notify('CAST_STATE', {
279289
isCasting: true,
280290
castSession: session,
291+
deviceName: deviceName,
292+
});
293+
294+
// Also add device name to audio state
295+
ChangeNotifier.notify('AUDIO_STATE', {
296+
castDevice: deviceName,
281297
});
282298

283299
// Load current track if available
@@ -299,10 +315,20 @@ class CastAdapter {
299315
if (session) {
300316
this.setupRemotePlayer();
301317

302-
// Notify state change
318+
// Get the receiver name (cast device name)
319+
const castDevice = session.getCastDevice();
320+
const deviceName = castDevice?.friendlyName || 'Cast Device';
321+
322+
// Notify state change with device name
303323
ChangeNotifier.notify('CAST_STATE', {
304324
isCasting: true,
305325
castSession: session,
326+
deviceName: deviceName,
327+
});
328+
329+
// Also add device name to audio state
330+
ChangeNotifier.notify('AUDIO_STATE', {
331+
castDevice: deviceName,
306332
});
307333
}
308334
}
@@ -320,14 +346,20 @@ class CastAdapter {
320346
ChangeNotifier.notify('CAST_STATE', {
321347
isCasting: false,
322348
castSession: null,
349+
deviceName: null,
350+
});
351+
352+
// Remove cast device name from audio state
353+
ChangeNotifier.notify('AUDIO_STATE', {
354+
castDevice: '',
323355
});
324356
}
325357

326358
/**
327359
* Initialize the remote player and controller.
328360
*/
329361
private initializeRemotePlayer(): void {
330-
if ((isValidWindow && !window.cast) || !window.cast.framework) {
362+
if (!isValidWindow || !window.cast?.framework) {
331363
console.error('Cast framework not available');
332364
return;
333365
}
@@ -438,7 +470,7 @@ class CastAdapter {
438470
console.log('Cast player state changed:', this.remotePlayer?.playerState);
439471

440472
let playbackState: string | undefined;
441-
if (!isValidWindow) {
473+
if (!isValidWindow || !window.chrome?.cast) {
442474
console.log('no valid window context, returning :: CAST::onPlayerStateChanged');
443475
return;
444476
}
@@ -552,7 +584,7 @@ class CastAdapter {
552584
private onMediaInfoChanged(): void {
553585
const session = this.getCastSession();
554586
const mediaSession = session?.getMediaSession();
555-
if (mediaSession) {
587+
if (mediaSession?.media) {
556588
console.log('Media info changed:', mediaSession.media);
557589
ChangeNotifier.notify('AUDIO_STATE', {
558590
mediaInfo: mediaSession.media,
@@ -571,16 +603,18 @@ class CastAdapter {
571603
const queue = mediaSession?.getQueueItems();
572604

573605
if (queue && currentItemId !== undefined) {
574-
const currentIndex = queue.findIndex((item: any) => item.itemId === currentItemId);
606+
const currentIndex = queue.findIndex(
607+
(item: chrome.cast.media.QueueItem) => item.itemId === currentItemId,
608+
);
575609
const nextTrack = queue[currentIndex + 1]?.media;
576610
if (nextTrack) {
577-
console.log(`Moving to next track in queue: ${nextTrack.metadata.title}`);
611+
console.log(`Moving to next track in queue: ${nextTrack.metadata?.title || 'Unknown'}`);
578612
ChangeNotifier.notify('AUDIO_STATE', {
579613
playbackState: PLAYBACK_STATE.PLAYING,
580614
currentTrack: {
581-
title: nextTrack.metadata.title,
582-
artist: nextTrack.metadata.artist,
583-
album: nextTrack.metadata.albumName,
615+
title: nextTrack.metadata?.title || 'Unknown Title',
616+
artist: nextTrack.metadata?.artist || 'Unknown Artist',
617+
album: nextTrack.metadata?.albumName || 'Unknown Album',
584618
source: nextTrack.contentId,
585619
},
586620
});
@@ -657,7 +691,7 @@ class CastAdapter {
657691
* @param mediaTrack - The media track to cast
658692
* @returns The created media info object
659693
*/
660-
private createCastMedia(mediaTrack: MediaTrack): any | null {
694+
private createCastMedia(mediaTrack: MediaTrack): chrome.cast.media.MediaInfo | null {
661695
try {
662696
if (!this.chromeCast) {
663697
console.error('Chrome Cast API not available');
@@ -757,17 +791,20 @@ class CastAdapter {
757791
*/
758792
loadQueue(tracks: MediaTrack[], startIndex = 0): void {
759793
const session = this.getCastSession();
760-
if (!session) {
794+
if (!session || !this.chromeCast) {
761795
console.error('No active cast session');
762796
return;
763797
}
764798

765-
const queueItems = tracks.map((track) => {
766-
const mediaInfo = this.createCastMedia(track);
767-
return new this.chromeCast!.media.QueueItem(mediaInfo);
768-
});
799+
const queueItems = tracks
800+
.map((track) => {
801+
const mediaInfo = this.createCastMedia(track);
802+
if (!mediaInfo || !this.chromeCast) return null;
803+
return new this.chromeCast.media.QueueItem(mediaInfo);
804+
})
805+
.filter((item): item is chrome.cast.media.QueueItem => item !== null);
769806

770-
const queueLoadRequest = new this.chromeCast!.media.QueueLoadRequest(queueItems);
807+
const queueLoadRequest = new this.chromeCast.media.QueueLoadRequest(queueItems);
771808
queueLoadRequest.startIndex = startIndex;
772809
queueLoadRequest.autoplay = true;
773810

@@ -821,13 +858,13 @@ class CastAdapter {
821858

822859
// Create media info for the next track
823860
const mediaInfo = this.createCastMedia(nextTrack);
824-
if (!mediaInfo) {
861+
if (!mediaInfo || !this.chromeCast) {
825862
console.error('Failed to create media info for next track');
826863
return;
827864
}
828865

829866
// Create load request with autoplay
830-
const request = new this.chromeCast!.media.LoadRequest(mediaInfo);
867+
const request = new this.chromeCast.media.LoadRequest(mediaInfo);
831868
request.autoplay = true;
832869

833870
// Load the next track
@@ -867,13 +904,13 @@ class CastAdapter {
867904

868905
// Create media info for the previous track
869906
const mediaInfo = this.createCastMedia(previousTrack);
870-
if (!mediaInfo) {
907+
if (!mediaInfo || !this.chromeCast) {
871908
console.error('Failed to create media info for previous track');
872909
return;
873910
}
874911

875912
// Create load request with autoplay
876-
const request = new this.chromeCast!.media.LoadRequest(mediaInfo);
913+
const request = new this.chromeCast.media.LoadRequest(mediaInfo);
877914
request.autoplay = true;
878915

879916
// Load the previous track
@@ -915,12 +952,12 @@ class CastAdapter {
915952
}
916953

917954
const mediaInfo = this.createCastMedia(track);
918-
if (!mediaInfo) {
955+
if (!mediaInfo || !this.chromeCast) {
919956
reject(new Error('Failed to create media info'));
920957
return;
921958
}
922959

923-
const request = new this.chromeCast!.media.LoadRequest(mediaInfo);
960+
const request = new this.chromeCast.media.LoadRequest(mediaInfo);
924961
request.autoplay = autoPlay;
925962

926963
session
@@ -1014,7 +1051,7 @@ class CastAdapter {
10141051
* Get the current cast session.
10151052
* @returns The current cast session or null if none exists
10161053
*/
1017-
getCastSession(): any | null {
1054+
getCastSession(): cast.framework.CastSession | null {
10181055
if (!this.castContext) {
10191056
return null;
10201057
}
@@ -1055,15 +1092,15 @@ class CastAdapter {
10551092
* Get the remote player instance.
10561093
* @returns The remote player instance
10571094
*/
1058-
getRemotePlayer(): any | null {
1095+
getRemotePlayer(): cast.framework.RemotePlayer | null {
10591096
return this.remotePlayer;
10601097
}
10611098

10621099
/**
10631100
* Get the remote player controller.
10641101
* @returns The remote player controller
10651102
*/
1066-
getRemotePlayerController(): any | null {
1103+
getRemotePlayerController(): cast.framework.RemotePlayerController | null {
10671104
return this.remotePlayerController;
10681105
}
10691106
}

src/states/audioState.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { PLAYBACK_STATE } from 'constants/common';
22
import { diffChecker } from 'helpers/common';
33
import ChangeNotifier from 'helpers/notifier';
4-
import { ReadyState } from 'types';
5-
import { AudioState, MediaTrack } from 'types/audio.types';
4+
import type { ReadyState } from 'types';
5+
import type { AudioState, MediaTrack } from 'types/audio.types';
66

77
export const READY_STATE: ReadyState = {
88
HAVE_NOTHING: 0,
99
HAVE_METADATA: 1,
1010
HAVE_CURRENT_DATA: 2,
1111
HAVE_FUTURE_DATA: 3,
12-
HAVE_ENOUGH_DATA: 4
12+
HAVE_ENOUGH_DATA: 4,
1313
};
1414

1515
export const AUDIO_STATE: AudioState = {
@@ -22,11 +22,12 @@ export const AUDIO_STATE: AudioState = {
2222
error: {
2323
code: null,
2424
message: '',
25-
readable: ''
25+
readable: '',
2626
},
2727
currentTrack: {} as MediaTrack,
2828
currentTrackPlayTime: 0,
29-
previousTrackPlayTime: 0
29+
previousTrackPlayTime: 0,
30+
castDevice: '',
3031
};
3132

3233
/* Listen to state changes and update global audio state that is being exposed to outer world
@@ -36,12 +37,10 @@ export const AUDIO_STATE: AudioState = {
3637
ChangeNotifier.listen(
3738
'AUDIO_STATE',
3839
(audioState: AudioState) => {
39-
const latestState = ChangeNotifier.getLatestState(
40-
'AUDIO_X_STATE'
41-
) as AudioState;
40+
const latestState = ChangeNotifier.getLatestState('AUDIO_X_STATE') as AudioState;
4241
if (!diffChecker(latestState, audioState)) {
4342
ChangeNotifier.notify('AUDIO_X_STATE', { ...AUDIO_STATE, ...audioState });
4443
}
4544
},
46-
AUDIO_STATE
45+
AUDIO_STATE,
4746
);

src/types/audio.types.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { HlsConfig } from 'types/hls.js';
2-
import { EventListenerCallbackMap } from './audioEvents.types';
3-
import { JoinPolicy } from './cast.types';
1+
import type { HlsConfig } from 'types/hls.js';
2+
import type { EventListenerCallbackMap } from './audioEvents.types';
3+
import type { JoinPolicy } from './cast.types';
44

55
export type InitMode = 'REACT' | 'VANILLA';
66
export type PlaybackRate = 1.0 | 1.25 | 1.5 | 1.75 | 2.0 | 2.5 | 3.0;
@@ -44,7 +44,7 @@ export interface AudioInit {
4444
enableHls?: boolean;
4545
enableEQ?: boolean;
4646
crossOrigin?: 'anonymous' | 'use-credentials' | null;
47-
hlsConfig?: HlsConfig | {};
47+
hlsConfig?: HlsConfig;
4848
enableCasting?: boolean;
4949
castConfig?: {
5050
receiverId: string;
@@ -69,6 +69,7 @@ export interface AudioState {
6969
currentTrack: MediaTrack;
7070
currentTrackPlayTime: number;
7171
previousTrackPlayTime: number;
72+
castDevice: string;
7273
}
7374

7475
export type QueuePlaybackType = 'DEFAULT' | 'REVERSE' | 'SHUFFLE';

0 commit comments

Comments
 (0)