@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;
}