@cassette/core
Version:
A simple, clean, and responsive visual wrapper for the HTML audio tag, built with React.
125 lines (120 loc) • 3.62 kB
JavaScript
import isPlaylistValid from './isPlaylistValid';
import getTrackSources from './getTrackSources';
import findTrackIndexByUrl from './findTrackIndexByUrl';
const veryLongKey =
'__highly_unstable_snapshot_internals_which_will_break_your_app_if_you_use_them_directly__';
const versionKey = '__cassette_snapshot_version__';
// IMPORTANT: new migrations *must* always be added to the end since
// the tracked snapshot version is based on the migration index.
// If there is a crash-inducing bug in an existing migration, it can be patched
// in-place, but it should never be removed from the migrations array.
const migrations = [
oldSnapshot => {
const { __unstable__, ...rest } = oldSnapshot;
return {
...rest,
[veryLongKey]: __unstable__
};
}
];
export function getStateSnapshot(state) {
const {
paused,
currentTime,
activeTrackIndex,
volume,
muted,
loop,
cycle,
shuffle,
playbackRate,
duration,
__playlist__
} = state;
return {
[versionKey]: migrations.length,
[veryLongKey]: {
paused,
// currentTime can't be restored for unbounded live streams
currentTime: duration === Infinity ? 0 : currentTime,
activeTrackIndex,
volume,
muted,
loop,
cycle,
shuffle,
playbackRate,
activeTrackSrc: isPlaylistValid(__playlist__)
? getTrackSources(__playlist__, activeTrackIndex)[0].src
: null
}
};
}
export function restoreStateFromSnapshot(snapshot, props) {
const migratedSnapshot = migrations
.slice(snapshot[versionKey] || 0)
.reduce((oldSnapshot, migration) => migration(oldSnapshot), snapshot);
const {
paused,
currentTime,
activeTrackIndex,
volume,
muted,
loop,
cycle,
shuffle,
playbackRate,
activeTrackSrc
} = migratedSnapshot[veryLongKey];
const restoredStateValues = {};
if (isPlaylistValid(props.playlist) && typeof paused === 'boolean') {
// using awaitingPlay instead of paused triggers an animation
restoredStateValues.awaitingPlay = !paused;
}
if (typeof volume === 'number' && volume >= 0 && volume <= 1) {
restoredStateValues.volume = volume;
}
if (typeof muted === 'boolean') {
restoredStateValues.muted = muted;
}
if (typeof loop === 'boolean') {
restoredStateValues.loop = loop;
}
if (typeof cycle === 'boolean') {
restoredStateValues.cycle = cycle;
}
if (typeof shuffle === 'boolean') {
restoredStateValues.shuffle = shuffle;
}
if (typeof playbackRate === 'number') {
restoredStateValues.playbackRate = playbackRate;
}
let useCurrentTime = false;
if (
typeof activeTrackSrc === 'string' &&
typeof activeTrackIndex === 'number' &&
activeTrackIndex >= 0
) {
// let's try staying on the same track index
const currentSrc =
props.playlist[activeTrackIndex] &&
getTrackSources(props.playlist, activeTrackIndex)[0].src;
if (currentSrc && activeTrackSrc === currentSrc) {
restoredStateValues.activeTrackIndex = activeTrackIndex;
useCurrentTime = true;
} else {
/* if the track we were playing before is in the new playlist,
* update the activeTrackIndex.
*/
const newTrackIndex = findTrackIndexByUrl(props.playlist, activeTrackSrc);
if (newTrackIndex !== -1) {
restoredStateValues.activeTrackIndex = newTrackIndex;
useCurrentTime = true;
}
}
}
if (useCurrentTime && typeof currentTime === 'number' && currentTime >= 0) {
restoredStateValues.currentTime = currentTime;
}
return restoredStateValues;
}