Skip to content
This repository was archived by the owner on Feb 28, 2026. It is now read-only.

Commit a70fc24

Browse files
committed
Fix loading status on stream loading and stuck pause
1 parent c187534 commit a70fc24

File tree

6 files changed

+52
-5
lines changed

6 files changed

+52
-5
lines changed

packages/hifi/src/Sound.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const Sound: React.FC<SoundProps> = ({
1818
onTimeUpdate,
1919
onEnd,
2020
onLoadStart,
21+
onCanPlay,
2122
onError,
2223
children,
2324
}) => {
@@ -28,7 +29,7 @@ export const Sound: React.FC<SoundProps> = ({
2829

2930
usePlaybackStatus(audioRef, status, context, isReady);
3031
useAudioSeek(audioRef, seek, isReady);
31-
useAudioLoader(audioRef, src, isReady);
32+
useAudioLoader(audioRef, src, status, isReady);
3233
useHlsSource(audioRef, src, isReady);
3334

3435
const { handleTimeUpdate, handleError } = useAudioEvents({
@@ -46,6 +47,7 @@ export const Sound: React.FC<SoundProps> = ({
4647
onTimeUpdate={handleTimeUpdate}
4748
onEnded={onEnd}
4849
onLoadStart={onLoadStart}
50+
onCanPlay={onCanPlay}
4951
onError={handleError}
5052
/>
5153
{isReady &&

packages/hifi/src/hooks/useAudioLoader.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { RefObject, useEffect, useRef } from 'react';
22

3-
import { AudioSource } from '../types';
3+
import { AudioSource, SoundStatus } from '../types';
44

55
export const useAudioLoader = (
66
audioRef: RefObject<HTMLAudioElement | null>,
77
src: AudioSource,
8+
status: SoundStatus,
89
isReady: boolean,
910
) => {
1011
const prevUrl = useRef<string | null>(null);
@@ -27,6 +28,10 @@ export const useAudioLoader = (
2728
audio.src = src.url;
2829
audio.load();
2930
prevUrl.current = src.url;
31+
32+
if (status === 'playing') {
33+
audio.play();
34+
}
3035
}
31-
}, [src, isReady, audioRef]);
36+
}, [src, status, isReady, audioRef]);
3237
};

packages/hifi/src/test/Sound.test.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { render } from '@testing-library/react';
22

33
import { Sound } from '../Sound';
44
import { AudioSource } from '../types';
5-
import { setupAudioContextMock } from './test-utils';
5+
import { resetMediaSpies, setupAudioContextMock } from './test-utils';
66

77
const httpSource: AudioSource = { url: '/a.mp3', protocol: 'http' };
88

@@ -20,4 +20,34 @@ describe('Sound', () => {
2020

2121
restore();
2222
});
23+
24+
it('calls play after loading a new source while status is playing', () => {
25+
const { restore } = setupAudioContextMock();
26+
const sourceA: AudioSource = { url: '/a.mp3', protocol: 'http' };
27+
const sourceB: AudioSource = { url: '/b.mp3', protocol: 'http' };
28+
29+
const { rerender } = render(<Sound src={sourceA} status="playing" />);
30+
31+
const { playMock } = resetMediaSpies();
32+
33+
rerender(<Sound src={sourceB} status="playing" />);
34+
35+
expect(playMock).toHaveBeenCalled();
36+
restore();
37+
});
38+
39+
it('does not call play after loading a new source while status is paused', () => {
40+
const { restore } = setupAudioContextMock();
41+
const sourceA: AudioSource = { url: '/a.mp3', protocol: 'http' };
42+
const sourceB: AudioSource = { url: '/b.mp3', protocol: 'http' };
43+
44+
const { rerender } = render(<Sound src={sourceA} status="paused" />);
45+
46+
const { playMock } = resetMediaSpies();
47+
48+
rerender(<Sound src={sourceB} status="paused" />);
49+
50+
expect(playMock).not.toHaveBeenCalled();
51+
restore();
52+
});
2353
});

packages/hifi/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type SoundProps = {
1515
onTimeUpdate?: (args: { position: number; duration: number }) => void;
1616
onEnd?: () => void;
1717
onLoadStart?: () => void;
18+
onCanPlay?: () => void;
1819
onError?: (error: Error) => void;
1920
children?: ReactNode;
2021
};

packages/player/src/components/SoundProvider.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ export const SoundProvider: FC<PropsWithChildren> = ({ children }) => {
3535
useQueueStore.getState().goToNext();
3636
}, []);
3737

38+
const handleCanPlay = useCallback(() => {
39+
const currentItem = useQueueStore.getState().getCurrentItem();
40+
if (currentItem) {
41+
useQueueStore
42+
.getState()
43+
.updateItemState(currentItem.id, { status: 'success' });
44+
}
45+
}, []);
46+
3847
return (
3948
<>
4049
{src && (
@@ -46,6 +55,7 @@ export const SoundProvider: FC<PropsWithChildren> = ({ children }) => {
4655
crossOrigin={crossOrigin}
4756
onTimeUpdate={handleTimeUpdate}
4857
onEnd={handleEnd}
58+
onCanPlay={handleCanPlay}
4959
>
5060
<Volume value={volumePercent} />
5161
</Sound>

packages/player/src/hooks/useStreamResolution.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ const resolveAndPlay = async (item: QueueItem, t: TFunction): Promise<void> => {
122122
return;
123123
}
124124

125-
updateItemState(item.id, { status: 'success' });
126125
setSrc(buildAudioSource(resolvedCandidate));
127126
play();
128127
};

0 commit comments

Comments
 (0)