Skip to content

Commit f830b39

Browse files
committed
feat(web): update the web implementation to conform to the new API
1 parent 8e5b32c commit f830b39

7 files changed

Lines changed: 200 additions & 161 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"android:ide": "cd apps/example/android && open -a 'Android Studio' .",
5555
"android:logs": "react-native log-android",
5656
"build": "yarn prepare",
57+
"build:watch": "tsc --watch --project tsconfig.build.json --outDir lib --declaration false --noEmit false",
5758
"clean": "del-cli android/build apps/example/android/build apps/example/android/app/build apps/example/ios/build lib",
5859
"example": "yarn workspace react-native-track-player-example",
5960
"format": "prettier --check src web",

src/NativeTrackPlayer.web.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
const module = require('../web').default;
2-
export const Constants = module?.getConstants();
3-
export default module;
1+
import webModule from '../web';
2+
export default webModule;

src/features/events.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,14 @@ const callbackByEvent = {
8282
'metadata-timed-received': TrackPlayer.onMetadataTimedReceived,
8383
'playback-state': TrackPlayer.onPlaybackState,
8484
'playback-playing-state': TrackPlayer.onPlaybackPlayingState,
85-
'playback-active-track': TrackPlayer.onPlaybackActiveTrack,
85+
// 'playback-active-track': TrackPlayer.onPlaybackActiveTrack,
8686
'playback-active-track-changed': TrackPlayer.onPlaybackActiveTrackChanged,
8787
'playback-progress-updated': TrackPlayer.onPlaybackProgressUpdated,
8888
'playback-play-when-ready-changed':
8989
TrackPlayer.onPlaybackPlayWhenReadyChanged,
9090
'playback-queue-ended': TrackPlayer.onPlaybackQueueEnded,
9191
'playback-error': TrackPlayer.onPlaybackError,
92-
'playback-metadata': TrackPlayer.onPlaybackMetadata,
92+
// 'playback-metadata': TrackPlayer.onPlaybackMetadata,
9393
'remote-play': TrackPlayer.onRemotePlay,
9494
'remote-play-search': TrackPlayer.onRemotePlaySearch,
9595
'remote-play-id': TrackPlayer.onRemotePlayId,
@@ -105,7 +105,7 @@ const callbackByEvent = {
105105
'remote-dislike': TrackPlayer.onRemoteDislike,
106106
'remote-bookmark': TrackPlayer.onRemoteBookmark,
107107
'remote-skip': TrackPlayer.onRemoteSkip,
108-
} satisfies Record<Event, (callback: unknown) => { remove: () => void }>;
108+
} satisfies Record<Event, (callback: any) => { remove: () => void }>;
109109

110110
/**
111111
* Subscribes to a TrackPlayer event.

web/TrackPlayer/Player.ts

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { State } from '../../src/constants/State';
2-
import type { PlaybackState, Progress, Track } from '../../src/types';
2+
import type { PlaybackState, Progress, Track } from '../../src/features';
33
import { SetupNotCalledError } from './SetupNotCalledError';
44

55
export class Player {
@@ -19,10 +19,10 @@ export class Player {
1919
}
2020

2121
// state getter/setter
22-
public get state(): PlaybackState {
22+
protected get state(): PlaybackState {
2323
return this._state;
2424
}
25-
public set state(newState: PlaybackState) {
25+
protected set state(newState: PlaybackState) {
2626
this._state = newState;
2727
}
2828

@@ -55,8 +55,10 @@ export class Player {
5555
this.state = {
5656
state: State.Error,
5757
error: {
58-
code: 'not_supported',
59-
message: 'Browser not supported.',
58+
error: {
59+
code: 'not_supported',
60+
message: 'Browser not supported...',
61+
},
6062
},
6163
};
6264
throw new Error('Browser not supported.');
@@ -65,6 +67,7 @@ export class Player {
6567
// build dom element and attach shaka-player
6668
this.element = document.createElement('audio');
6769
this.element.setAttribute('id', 'react-native-track-player');
70+
document.body.appendChild(this.element);
6871
this.player = new shaka.Player();
6972
this.player?.attach(this.element);
7073

@@ -98,6 +101,8 @@ export class Player {
98101
this.player!.addEventListener('buffering', ({ buffering }: any) => {
99102
if (buffering === true) {
100103
this.onStateUpdate(State.Buffering);
104+
} else {
105+
this.onStateUpdate(State.Ready);
101106
}
102107
});
103108

@@ -120,8 +125,10 @@ export class Player {
120125
this.state = {
121126
state: State.Error,
122127
error: {
123-
code: error.code.toString(),
124-
message: error.message,
128+
error: {
129+
code: error.code.toString(),
130+
message: error.message,
131+
},
125132
},
126133
};
127134

@@ -130,36 +137,54 @@ export class Player {
130137
}
131138

132139
/**
133-
* player control
140+
* NOTE: this method is sync despite the actual load being async. This
141+
* behavior is intentional as it mirrors what happens in Android. State
142+
* changes should be captured by event listeners.
134143
*/
135-
public async load(track: Track) {
136-
if (!this.player) throw new SetupNotCalledError();
137-
await this.player.load(track.url as string);
138-
this.current = track;
139-
}
140-
141-
public async retry() {
144+
public load(
145+
track: Track,
146+
onComplete?: (track: Track) => void,
147+
) {
142148
if (!this.player) throw new SetupNotCalledError();
143-
this.player.retryStreaming();
149+
this.player.load(track.url as string)
150+
.then(() => {
151+
this.current = track;
152+
onComplete?.(track)
153+
});
144154
}
145155

146-
public async stop() {
156+
/**
157+
* NOTE: this method is sync despite the actual load being async. This
158+
* behavior is intentional as it mirrors what happens in Android. State
159+
* changes should be captured by event listeners.
160+
*/
161+
public stop(onComplete?: () => void) {
147162
if (!this.player) throw new SetupNotCalledError();
148163
this.current = undefined;
149-
await this.player.unload();
164+
this.player.unload()
165+
.then(() => onComplete?.());
150166
}
151167

168+
/**
169+
* NOTE: this method is sync despite the actual load being async. This
170+
* behavior is intentional as it mirrors what happens in Android. State
171+
* changes should be captured by event listeners.
172+
*/
152173
public play() {
153174
if (!this.element) throw new SetupNotCalledError();
154175
this.playWhenReady = true;
155-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
156-
return this.element.play().catch((err: any) => console.error(err));
176+
this.element.play().catch((err: unknown) => console.error(err));
177+
}
178+
179+
public retry() {
180+
if (!this.player) throw new SetupNotCalledError();
181+
this.player.retryStreaming();
157182
}
158183

159184
public pause() {
160185
if (!this.element) throw new SetupNotCalledError();
161186
this.playWhenReady = false;
162-
return this.element.pause();
187+
this.element.pause();
163188
}
164189

165190
public setRate(rate: number) {

web/TrackPlayer/PlaylistPlayer.ts

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Player } from './Player';
22

33
import { State } from '../../src/constants/State';
4-
import type { Track } from '../../src/types';
4+
import type { Track } from '../../src/features';
55
import { RepeatMode } from './RepeatMode';
66

77
export class PlaylistPlayer extends Player {
@@ -11,31 +11,31 @@ export class PlaylistPlayer extends Player {
1111
protected _currentIndex?: number;
1212
protected repeatMode: RepeatMode = RepeatMode.Off;
1313

14-
protected async onStateUpdate(state: Exclude<State, State.Error>) {
14+
protected onStateUpdate(state: Exclude<State, State.Error>) {
1515
super.onStateUpdate(state);
1616

1717
if (state === State.Ended) {
18-
await this.onTrackEnded();
18+
this.onTrackEnded();
1919
}
2020
}
2121

22-
protected async onTrackEnded() {
22+
protected onTrackEnded() {
2323
switch (this.repeatMode) {
2424
case RepeatMode.Track:
2525
if (this.currentIndex !== undefined) {
26-
await this.goToIndex(this.currentIndex);
26+
this.goToIndex(this.currentIndex);
2727
}
2828
break;
2929
case RepeatMode.Playlist:
3030
if (this.currentIndex === this.playlist.length - 1) {
31-
await this.goToIndex(0);
31+
this.goToIndex(0);
3232
} else {
33-
await this.skipToNext();
33+
this.skipToNext();
3434
}
3535
break;
3636
default:
3737
try {
38-
await this.skipToNext();
38+
this.skipToNext();
3939
} catch (err) {
4040
if ((err as Error).message !== 'playlist_exhausted') {
4141
throw err;
@@ -59,61 +59,65 @@ export class PlaylistPlayer extends Player {
5959
this._currentIndex = current;
6060
}
6161

62-
protected async goToIndex(index: number, initialPosition?: number) {
62+
protected goToIndex(index: number, initialPosition?: number) {
6363
const track = this.playlist[index];
6464

6565
if (!track) {
6666
throw new Error('playlist_exhausted');
6767
}
6868

69-
if (this.currentIndex !== index) {
70-
this.currentIndex = index;
71-
await this.load(track);
72-
}
69+
const onCompletedLoading = () => {
70+
if (initialPosition) {
71+
this.seekTo(initialPosition);
72+
}
7373

74-
if (initialPosition) {
75-
this.seekTo(initialPosition);
76-
}
74+
if (this.playWhenReady) {
75+
this.play();
76+
}
77+
};
7778

78-
if (this.playWhenReady) {
79-
await this.play();
79+
if (this.currentIndex !== index) {
80+
this.currentIndex = index;
81+
this.load(track, onCompletedLoading);
82+
} else {
83+
onCompletedLoading();
8084
}
8185
}
8286

83-
public async add(tracks: Track[], insertBeforeIndex?: number) {
87+
public add(tracks: Track[], insertBeforeIndex?: number) {
8488
if (insertBeforeIndex !== -1 && insertBeforeIndex !== undefined) {
8589
this.playlist.splice(insertBeforeIndex, 0, ...tracks);
8690
} else {
8791
this.playlist.push(...tracks);
8892
}
8993

9094
if (this.currentIndex === undefined) {
91-
await this.goToIndex(0);
95+
this.goToIndex(0);
9296
}
9397
}
9498

95-
public async skip(index: number, initialPosition?: number) {
99+
public skip(index: number, initialPosition?: number) {
96100
const track = this.playlist[index];
97101

98102
if (track === undefined) {
99103
throw new Error('index out of bounds');
100104
}
101105

102-
await this.goToIndex(index, initialPosition);
106+
this.goToIndex(index, initialPosition);
103107
}
104108

105-
public async skipToNext(initialPosition?: number) {
109+
public skipToNext(initialPosition?: number) {
106110
if (this.currentIndex === undefined) return;
107111

108112
const index = this.currentIndex + 1;
109-
await this.goToIndex(index, initialPosition);
113+
this.goToIndex(index, initialPosition);
110114
}
111115

112-
public async skipToPrevious(initialPosition?: number) {
116+
public skipToPrevious(initialPosition?: number) {
113117
if (this.currentIndex === undefined) return;
114118

115119
const index = this.currentIndex - 1;
116-
await this.goToIndex(index, initialPosition);
120+
this.goToIndex(index, initialPosition);
117121
}
118122

119123
public getTrack(index: number): Track | null {
@@ -129,7 +133,7 @@ export class PlaylistPlayer extends Player {
129133
return this.repeatMode;
130134
}
131135

132-
public async remove(indexes: number[]) {
136+
public remove(indexes: number[]) {
133137
const idxMap = indexes.reduce<Record<number, boolean>>((acc, elem) => {
134138
acc[elem] = true;
135139
return acc;
@@ -151,28 +155,31 @@ export class PlaylistPlayer extends Player {
151155

152156
const hasItems = this.playlist.length > 0;
153157
if (isCurrentRemoved && hasItems) {
154-
await this.goToIndex(this.currentIndex % this.playlist.length);
158+
this.goToIndex(this.currentIndex % this.playlist.length);
155159
} else if (isCurrentRemoved) {
156-
await this.stop();
160+
this.stop();
157161
}
158162
}
159163

160-
public async stop() {
161-
await super.stop();
162-
this.currentIndex = undefined;
164+
public stop(onComplete?: () => void) {
165+
super.stop(() => {
166+
this.currentIndex = undefined;
167+
onComplete?.();
168+
});
163169
}
164170

165-
public async reset() {
166-
await this.stop();
167-
this.playlist = [];
171+
public reset() {
172+
this.stop(() => {
173+
this.playlist = [];
174+
});
168175
}
169176

170-
public async removeUpcomingTracks() {
177+
public removeUpcomingTracks() {
171178
if (this.currentIndex === undefined) return;
172179
this.playlist = this.playlist.slice(0, this.currentIndex + 1);
173180
}
174181

175-
public async move(fromIndex: number, toIndex: number): Promise<void> {
182+
public move(fromIndex: number, toIndex: number): void {
176183
if (!this.playlist[fromIndex]) {
177184
throw new Error('index out of bounds');
178185
}

0 commit comments

Comments
 (0)