Skip to content

Commit

Permalink
Add the "preloadData" prop
Browse files Browse the repository at this point in the history
- update tests
- update README
  • Loading branch information
gilbarbara committed Nov 13, 2024
1 parent 34a1c27 commit 7cc8bc7
Show file tree
Hide file tree
Showing 19 changed files with 701 additions and 115 deletions.
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,17 @@ Save the device selection.
**play** `boolean`
Control the player's status.

**preloadData** `boolean`
Preload the track data before playing.

**showSaveIcon** `boolean` ▶︎ false
Display a Favorite button. It needs additional scopes in your token.

**styles** `object`
Customize the player's appearance. Check `StylesOptions` in the [types](src/types/common.ts).

**syncExternalDevice** `boolean` ▶︎ false
If there are no URIs and an external device is playing, use the external player context.
Use the external player context if there are no URIs and an external device is playing.

**syncExternalDeviceInterval** `number` ▶︎ 5
The time in seconds that the player will sync with external devices.
Expand All @@ -252,12 +255,24 @@ import { spotifyApi } from 'react-spotify-web-playback';

**checkTracksStatus(token: string, tracks: string | string[]): Promise\<boolean[]>**

**getAlbumTracks(token: string, id: string): Promise\<SpotifyApi.AlbumTracksResponse>**

**getArtistTopTracks(token: string, id: string): Promise\<SpotifyApi.ArtistsTopTracksResponse>**

**getDevices(token: string): Promise\<SpotifyApi.UserDevicesResponse>**

**getPlaybackState(token: string): Promise\<SpotifyApi.CurrentlyPlayingObject | null>**

**getPlaylistTracks(token: string, id: string): Promise\<SpotifyApi.PlaylistTrackResponse>**

**getQueue(token: string): Promise\<SpotifyApi.UsersQueueResponse>**

**getShow(token: string, id: string): Promise\<SpotifyApi.ShowObjectFull>**

**getShowEpisodes(token: string, id: string, offset = 0): Promise\<SpotifyApi.ShowEpisodesResponse>**

**getTrack(token: string, id: string): Promise\<SpotifyApi.TrackObjectFull>**

**pause(token: string, deviceId?: string): Promise\<void>**

**play(token: string, options: SpotifyPlayOptions): Promise\<void>**
Expand Down Expand Up @@ -293,7 +308,7 @@ interface SpotifyPlayOptions {

You can customize the UI with a `styles` prop.
If you want a transparent player, you can use `bgColor: 'transparent'`.
Check all the available options [here](src/types/common.ts#L188).
Check all the available options [here](src/types/common.ts#L195).

```tsx
<SpotifyWebPlayer
Expand All @@ -310,7 +325,8 @@ Check all the available options [here](src/types/common.ts#L188).
/>
```

![rswp-styles](https://gilbarbara.com/files/rswp-styles-e4060ddf.png)
![rswp-styles](https://files.gilbarbara.dev/media/rswp-v2.png)


## Issues

Expand Down
1 change: 0 additions & 1 deletion src/components/Devices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ export default function Devices(props: Props) {
const handleClickSetDevice = (event: MouseEvent<HTMLElement>) => {
const { dataset } = event.currentTarget;

/* istanbul ignore else */
if (dataset.id) {
onClickDevice(dataset.id);

Expand Down
1 change: 0 additions & 1 deletion src/components/Info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,6 @@ function Info(props: Props) {
const title = getSpotifyLinkTitle(name, locale.title);
let favorite;

/* istanbul ignore else */
if (showSaveIcon && id) {
favorite = (
<button
Expand Down
9 changes: 9 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ export const ERROR_TYPE = {
PLAYER: 'player',
} as const;

export const SPOTIFY_CONTENT_TYPE = {
ALBUM: 'album',
ARTIST: 'artist',
PLAYLIST: 'playlist',
SHOW: 'show',
TRACK: 'track',
};

export const STATUS = {
ERROR: 'ERROR',
IDLE: 'IDLE',
Expand All @@ -21,6 +29,7 @@ export const TYPE = {
DEVICE: 'device_update',
FAVORITE: 'favorite_update',
PLAYER: 'player_update',
PRELOAD: 'preload_update',
PROGRESS: 'progress_update',
STATUS: 'status_update',
TRACK: 'track_update',
Expand Down
80 changes: 42 additions & 38 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import { createRef, PureComponent, ReactNode } from 'react';
import isEqual from '@gilbarbara/deep-equal';
import memoize from 'memoize-one';

import { getLocale, getMergedStyles, getSpotifyURIType } from '~/modules/getters';
import {
convertTrack,
getAlbumImage,
getArrayOfStrings,
getItemImage,
getLocale,
getMergedStyles,
getPreloadData,
getRepeatState,
getURIs,
loadSpotifyPlayer,
parseVolume,
round,
validateURI,
} from '~/modules/helpers';
getSpotifyURIType,
getTrackInfo,
} from '~/modules/getters';
import { loadSpotifyPlayer, parseVolume, round, validateURI } from '~/modules/helpers';
import {
getDevices,
getPlaybackState,
Expand Down Expand Up @@ -112,7 +112,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
uris: undefined,
};

/* istanbul ignore else */
if (ids) {
if (!ids.every(d => validateURI(d))) {
return playOptions;
Expand Down Expand Up @@ -220,15 +219,14 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
uris,
} = this.props;
const isReady = previousState.status !== STATUS.READY && status === STATUS.READY;
const playOptions = this.getPlayOptions(getURIs(uris));
const playOptions = this.getPlayOptions(getArrayOfStrings(uris));

const canPlay = !!currentDeviceId && !!(playOptions.context_uri ?? playOptions.uris);
const shouldPlay = isReady && (autoPlay || playProp);

if (canPlay && shouldPlay) {
await this.togglePlay(true);

/* istanbul ignore else */
if (!isPlaying) {
this.updateState({ isPlaying: true });
}
Expand Down Expand Up @@ -267,7 +265,7 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
await this.updateSeekBar();
}

if (previousState.track.id !== track.id && track.id) {
if (track.id && previousState.track.id !== track.id) {
this.handleCallback({
...this.state,
type: TYPE.TRACK,
Expand Down Expand Up @@ -303,7 +301,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
if (syncExternalDevice && !uris) {
const playerState = await getPlaybackState(this.token);

/* istanbul ignore else */
if (playerState?.is_playing && playerState.device.id !== deviceId) {
this.setExternalDevice(playerState.device.id ?? '');
}
Expand All @@ -326,7 +323,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
public async componentWillUnmount() {
this.isMounted = false;

/* istanbul ignore else */
if (this.player) {
this.player.disconnect();
}
Expand Down Expand Up @@ -409,7 +405,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {

private handleClickPrevious = async () => {
try {
/* istanbul ignore else */
if (this.isExternalPlayer) {
await previous(this.token);
this.syncTimeout = window.setTimeout(() => {
Expand All @@ -426,7 +421,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {

private handleClickNext = async () => {
try {
/* istanbul ignore else */
if (this.isExternalPlayer) {
await next(this.token);
this.syncTimeout = window.setTimeout(() => {
Expand All @@ -450,12 +444,10 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
try {
await setDevice(this.token, deviceId);

/* istanbul ignore else */
if (persistDeviceSelection) {
sessionStorage.setItem('rswpDeviceId', deviceId);
}

/* istanbul ignore else */
if (isUnsupported) {
await this.syncDevice();

Expand All @@ -476,7 +468,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {

this.updateState({ isSaved: status });

/* istanbul ignore else */
if (isSaved !== status) {
this.handleCallback({
...this.state,
Expand Down Expand Up @@ -521,7 +512,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
const { currentURI } = this.state;

try {
/* istanbul ignore else */
if (state) {
const {
paused,
Expand All @@ -538,10 +528,10 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
if ((!currentURI || currentURI !== current_track.uri) && current_track) {
trackState = {
currentURI: current_track.uri,
nextTracks: next_tracks.map(convertTrack),
nextTracks: next_tracks.map(getTrackInfo),
position: 0,
previousTracks: previous_tracks.map(convertTrack),
track: convertTrack(current_track),
previousTracks: previous_tracks.map(getTrackInfo),
track: getTrackInfo(current_track),
};
}

Expand Down Expand Up @@ -591,6 +581,10 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
isInitializing: false,
status: device_id ? STATUS.READY : STATUS.IDLE,
});

if (device_id) {
await this.preload();
}
};

private handleResize = () => {
Expand Down Expand Up @@ -628,7 +622,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
if (persistDeviceSelection) {
const savedDeviceId = sessionStorage.getItem('rswpDeviceId');

/* istanbul ignore else */
if (!savedDeviceId || !devices.some((d: SpotifyDevice) => d.id === savedDeviceId)) {
sessionStorage.setItem('rswpDeviceId', currentDeviceId);
} else {
Expand Down Expand Up @@ -698,12 +691,30 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
return (currentDeviceId && currentDeviceId !== deviceId) || status === STATUS.UNSUPPORTED;
}

private preload = async () => {
const { offset = 0, preloadData, uris } = this.props;

if (!preloadData) {
return;
}

const track = await getPreloadData(this.token, uris, offset);

if (track) {
this.updateState({ track }, () => {
this.handleCallback({
...this.state,
type: TYPE.PRELOAD,
});
});
}
};

private setExternalDevice = (id: string) => {
this.updateState({ currentDeviceId: id, isPlaying: true });
};

private setVolume = async (volume: number) => {
/* istanbul ignore else */
if (this.isExternalPlayer) {
await setVolume(this.token, Math.round(volume * 100));
await this.syncDevice();
Expand All @@ -729,13 +740,12 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
throw new Error('No player');
}

/* istanbul ignore else */
if (playerState.item) {
track = {
artists: 'artists' in playerState.item ? playerState.item.artists : [],
durationMs: playerState.item.duration_ms,
id: playerState.item.id,
image: 'album' in playerState.item ? getAlbumImage(playerState.item.album) : '',
image: 'album' in playerState.item ? getItemImage(playerState.item.album) : '',
name: playerState.item.name,
uri: playerState.item.uri,
};
Expand Down Expand Up @@ -806,9 +816,7 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
private toggleProgressBar() {
const { isPlaying } = this.state;

/* istanbul ignore else */
if (isPlaying) {
/* istanbul ignore else */
if (!this.playerProgressInterval) {
this.playerProgressInterval = window.setInterval(
this.updateSeekBar,
Expand All @@ -824,7 +832,7 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
private toggleOffset = async () => {
const { currentDeviceId } = this.state;
const { offset, uris } = this.props;
const playOptions = this.getPlayOptions(getURIs(uris));
const playOptions = this.getPlayOptions(getArrayOfStrings(uris));

if (typeof offset === 'number') {
await play(this.token, { deviceId: currentDeviceId, offset, ...playOptions });
Expand All @@ -835,10 +843,9 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
const { currentDeviceId, isPlaying, needsUpdate } = this.state;
const { offset, uris } = this.props;
const shouldInitialize = force || needsUpdate;
const playOptions = this.getPlayOptions(getURIs(uris));
const playOptions = this.getPlayOptions(getArrayOfStrings(uris));

try {
/* istanbul ignore else */
if (this.isExternalPlayer) {
if (!isPlaying) {
await play(this.token, {
Expand Down Expand Up @@ -890,7 +897,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
const { progressMs, track } = this.state;

try {
/* istanbul ignore else */
if (this.isExternalPlayer) {
let position = progressMs / track.durationMs;

Expand All @@ -903,7 +909,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
} else if (this.player) {
const state = await this.player.getCurrentState();

/* istanbul ignore else */
if (state) {
const progress = state.position;
const position = Number(
Expand All @@ -922,12 +927,12 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
}
};

private updateState: typeof this.setState = state => {
private updateState: typeof this.setState = (state, callback) => {
if (!this.isMounted) {
return;
}

this.setState(state);
this.setState(state, callback);
};

public render() {
Expand Down Expand Up @@ -964,7 +969,6 @@ class SpotifyWebPlayer extends PureComponent<Props, State> {
};

if (isReady) {
/* istanbul ignore else */
if (!output.info) {
output.info = (
<Info
Expand Down
Loading

0 comments on commit 7cc8bc7

Please sign in to comment.