Source: utils/PlayerUtils.js

import * as Events from './Events.js';

import SRGStreamType from './SRGStreamType.js';
import SupportedDevices from './SupportedDevices.js';
import Utils from './Utils.js';
import TextTracksUtils from './TextTracksUtils.js';
import AudioTracksUtils from './AudioTracksUtils.js';

function autoPlay(player, value) {
  if (value !== undefined) {
    player.trigger(Events.AUTOPLAY_CHANGE);
  }

  return player.autoplay(value);
}

function getDebugInformation(player) {
  const {
    downlink,
    effectiveType,
    rtt,
  } = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {};

  const { ilHost, SRGProviders: { mediaComposition = {} } = {}, version } = player.options();

  const { lastPerformanceReportId } = player.getCache();

  const {
    mediaType, quality, src, streamType, streaming, tokenType, type,
  } = player.currentSource();

  const error = player.error();

  return {
    date: new Date().toISOString(),
    urn: mediaComposition.chapterUrn || src,
    ilHost,
    version,
    device: SupportedDevices.bowser().getResult(),
    url: window.location.href,
    connection: { downlink, effectiveType, rtt },
    lastPerformanceReportId,
    source: {
      mediaType, quality, streamType, streaming, tokenType, type,
    },
    error,
  };
}

function currentDate(date, player) {
  const liveCurrentTime = player.liveTracker.liveCurrentTime();

  if (!date) {
    const currentTime = Utils.secondsToMilliseconds(liveCurrentTime - player.currentTime());

    return new Date(Date.now() - currentTime);
  }

  const dateInRange = Utils.dateWithinLiveWindow(date, player.liveTracker.liveWindow());
  const { streamOffset = 0 } = player.currentSource();
  const streamOffsetInseconds = Utils.millisecondsToSeconds(streamOffset);
  const timeDiffInseconds = Utils.millisecondsToSeconds(Date.now() - dateInRange.getTime());

  return player.currentTime(liveCurrentTime - timeDiffInseconds + streamOffsetInseconds);
}

function filterTextTrack(player) {
  return Array.from(player.textTracks())
    .filter(({ kind, language, srgKind }) => srgKind !== 'forced' && !['metadata', 'forced'].includes(kind) && language.length);
}


function showTextTrack(track) {
  if (track) {
    const textTrack = track;
    textTrack.mode = 'showing';
  }
}

function hideTextTracks(tracks) {
  tracks.forEach((track) => {
    const textTrack = track;
    textTrack.mode = 'disabled';
  });
}

function findTextTrack({ language, captions }, tracks) {
  const kind = captions ? 'captions' : 'subtitles';

  let currentTextTrack = tracks
    .find(track => (track.language === language && track.srgKind === kind));

  if (!currentTextTrack) {
    currentTextTrack = tracks.find(track => track.language === language);
  }

  return currentTextTrack;
}

function textTrackLanguage({ language, captions } = {}, player) {
  const tracks = filterTextTrack(player);

  if (language !== undefined && tracks.length) {
    hideTextTracks(tracks);

    const currentTextTrack = findTextTrack({ language, captions }, tracks);

    showTextTrack(currentTextTrack);
  }

  const currentTrack = TextTracksUtils.findActiveTrack(tracks);

  return TextTracksUtils.format(currentTrack);
}

function textTrackSizeLevel(sizeLevel, player) {
  const { srgTextTrackSizes } = player.options();
  const fontSize = srgTextTrackSizes[sizeLevel];
  const textTrackDisplay = player.getChild('textTrackDisplay');

  if (textTrackDisplay && fontSize) {
    textTrackDisplay.setFontSize(fontSize);
  }

  return textTrackDisplay
    && textTrackDisplay.fontSize;
}

function audioTrackLanguage({ language, description } = {}, player) {
  const audioTracks = Array.from(player.audioTracks());

  if (language && !AudioTracksUtils.hasLanguageTrack(language, audioTracks)) return undefined;

  if (language && AudioTracksUtils.hasLanguageTrack(language, audioTracks)) {
    AudioTracksUtils.enableTrack({ language, description }, audioTracks);
  }

  const currentActiveTrack = AudioTracksUtils.findActiveTrack(audioTracks);

  return AudioTracksUtils.format(currentActiveTrack);
}

function isStreamOnDemand(player) {
  return SRGStreamType.isOnDemand(player.currentSource().streamType);
}

function getComponent(path, player) {
  return path.reduce((item, nextItem) => {
    if (!item || !item.getChild) return undefined;

    return item.getChild(nextItem);
  }, player);
}

function getPrepareToPlayEvent(data) {
  return {
    type: Events.PREPARE_TO_PLAY,
    data,
  };
}

function getUrn(player) {
  const { mediaComposition: { segmentUrn, chapterUrn } = {} } = player.options().SRGProviders;
  return segmentUrn || chapterUrn;
}

function checkAndToggleDvrTracking(player) {
  player.one(Events.FIRST_PLAY, () => { player.liveTracker.toggleTracking(); });
}

function loadMedia(player, {
  urn,
  mediaComposition,
  pendingSeek,
  playbackSettings = {},
  autoplay,
  standalone = false,
  periodicReload = false,
  readyCallback = () => { },
} = {}) {
  // Used in analytics to notify a stop event
  if (Object.keys(player.currentSource()).length) {
    player.trigger(Events.EMPTIED);
  }

  player.trigger(getPrepareToPlayEvent({ ready: false }));

  /* eslint-disable */
  if (player.tech_) {
    player.tech_.clearTracks('text');
  }

  player.loadTech_(player.options_.techOrder[0], null);
  player.techCall_('reset');
  /* eslint-enable */

  checkAndToggleDvrTracking(player);

  player.src({
    type: 'srgssr/urn',
    src: urn,
    mediaComposition,
    pendingSeek,
    periodicReload,
    playbackSettings,
    standalone,
  });

  player.ready(() => {
    player.trigger(getPrepareToPlayEvent({ ready: true }));

    readyCallback();

    if (playbackSettings.forcePlay) {
      player.play();
    } else {
      const cacheAutoplay = autoplay !== undefined ? autoplay : player.autoplay();

      autoPlay(player, cacheAutoplay);
    }

    // TODO: Extract to SRGLetterboxLayout
    if (SupportedDevices.isTouchDevice) {
      player.addClass('srgssr-touch-enabled');
    } else {
      player.removeClass('srgssr-touch-enabled');
    }
  });
}

function seekToSegment(player, segment) {
  const time = segment.markIn >= 0 ? Utils.millisecondsToSeconds(segment.markIn) : undefined;

  player.currentTime(time);

  player.trigger({
    type: Events.SEGMENT_SWITCHED,
    data: {
      analyticsMetadata: segment.analyticsMetadata,
      origin: 'click',
      playing: !player.paused() && player.hasStarted(),
    },
  });
}

function switchToUrn(player, urn) {
  const { mediaComposition } = player.options().SRGProviders;
  const [segment] = mediaComposition
    .getMainSegments()
    .filter(mainSegment => mainSegment.urn === urn);

  if (segment) {
    seekToSegment(player, segment);
    player.play();
  } else {
    loadMedia(player, { urn, autoplay: true });
  }
}

/**
 * Activate or deactivate the inactivity timeout.
 *
 * Allows to control the display of the player's UI.
 *
 * @param {VideoJsPlayer} player
 * @param {Boolean} trackUserActivity
 * @returns {Boolean|undefined}
 */
function reportUserActivity(player, trackUserActivity) {
  if (trackUserActivity === undefined || typeof trackUserActivity !== 'boolean') {
    return player.getCache().reportUserActivity;
  }

  const inactivityTimeout = trackUserActivity ? 2000 : 0;

  player.getCache().reportUserActivity = trackUserActivity; // eslint-disable-line no-param-reassign
  player.getCache().inactivityTimeout = inactivityTimeout; // eslint-disable-line no-param-reassign
  player.options({ inactivityTimeout });

  if (trackUserActivity) {
    player.setTimeout(() => {
      player.userActive(false);
    }, inactivityTimeout);
  } else {
    player.userActive(true);
  }

  return trackUserActivity;
}

export default {
  autoplay: autoPlay,
  currentDate,
  audioTrackLanguage,
  filterTextTrack,
  getPrepareToPlayEvent,
  getComponent,
  getDebugInformation,
  getUrn,
  isStreamOnDemand,
  loadMedia,
  reportUserActivity,
  showTextTrack,
  switchToUrn,
  textTrackLanguage,
  textTrackSizeLevel,
  seekToSegment,
};