1

I'm trying to follow this IVS sample, the only difference is using the JW player, I following this documentation

The problem is that this snippet

  jwplayer(videoPlayer).addEventListener(
    PlayerEventType.TEXT_METADATA_CUE,
    function (cue) {
      const metadataText = cue.text;
      const position = player.getPosition().toFixed(2);
      console.log(
        `Player Event - TEXT_METADATA_CUE: "${metadataText}". Observed ${position}s after playback started.`
      );
      triggerQuiz(metadataText);
    }
  );

is giving this error

script.js:60 Uncaught TypeError: Cannot read properties of undefined (reading 'TEXT_METADATA_CUE')

I'm either not adding an event listener to the jw player correctly or I'm doing something wrong while working with IVS.

My full code is below

const playbackUrl =
  "https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.xhP3ExfcX8ON.m3u8";

const videoPlayer = document.getElementById("video-player");
const quizEl = document.getElementById("quiz");
const waitMessage = document.getElementById("waiting");
const questionEl = document.getElementById("question");
const answersEl = document.getElementById("answers");
const cardInnerEl = document.getElementById("card-inner");
var ivsPlayer = {};
var ivsEvents = {};
const ivsConfig = {
  playlist: [
    {
      file: playbackUrl,
      type: "ivs",
    },
  ],
};

(function (ivsPlayer) {
  jwplayer(videoPlayer)
    .setup(ivsConfig)
    .on("providerPlayer", function (player) {
      console.log("Amazon IVS Player: ", player.ivsPlayer);
      console.log("Amazon IVS Player Events: ", player.ivsEvents);

      // store the reference to the Amazon IVS Player
      ivsPlayer = player.ivsPlayer;
      // store the reference to the Amazon IVS Player Events
      ivsEvents = player.ivsEvents;
    });
  const PlayerState = ivsPlayer.PlayerState;
  const PlayerEventType = ivsPlayer.PlayerEventType;
 

  jwplayer(videoPlayer).addEventListener(
    PlayerEventType.TEXT_METADATA_CUE,
    function (cue) {
      const metadataText = cue.text;
      const position = player.getPosition().toFixed(2);
      console.log(
        `Player Event - TEXT_METADATA_CUE: "${metadataText}". Observed ${position}s after playback started.`
      );
      triggerQuiz(metadataText);
    }
  );

  // Setup stream and play

  // Remove card
  function removeCard() {
    quizEl.classList.toggle("drop");
  }

  // Trigger quiz
  function triggerQuiz(metadataText) {
    let obj = JSON.parse(metadataText);

    quizEl.style.display = "";
    quizEl.classList.remove("drop");
    waitMessage.style.display = "none";
    cardInnerEl.style.display = "none";
    cardInnerEl.style.pointerEvents = "auto";

    while (answersEl.firstChild) answersEl.removeChild(answersEl.firstChild);
    questionEl.textContent = obj.question;

    let createAnswers = function (obj, i) {
      let q = document.createElement("a");
      let qText = document.createTextNode(obj.answers[i]);
      answersEl.appendChild(q);
      q.classList.add("answer");
      q.appendChild(qText);

      q.addEventListener("click", (event) => {
        cardInnerEl.style.pointerEvents = "none";
        if (q.textContent === obj.answers[obj.correctIndex]) {
          q.classList.toggle("correct");
        } else {
          q.classList.toggle("wrong");
        }
        setTimeout(function () {
          removeCard();
          waitMessage.style.display = "";
        }, 1050);
        return false;
      });
    };

    for (var i = 0; i < obj.answers.length; i++) {
      createAnswers(obj, i);
    }
    cardInnerEl.style.display = "";
  }

  waitMessage.style.display = "";
})(window.ivsPlayer);

Edit see the snippet

const playbackUrl =
  "https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.xhP3ExfcX8ON.m3u8";
const ivsConfig = {
  playlist: [
    {
      file: playbackUrl,
      type: "ivs",
    },
  ],
};

const videoPlayer = document.getElementById("video-player");
const quizEl = document.getElementById("quiz");
const waitMessage = document.getElementById("waiting");
const questionEl = document.getElementById("question");
const answersEl = document.getElementById("answers");
const cardInnerEl = document.getElementById("card-inner");

(async (IVSPlayer) => {
  try {
    const playerInstance = jwplayer(videoPlayer).setup(ivsConfig);

    playerInstance.on("providerPlayer", function (player) {
      console.log("Amazon IVS Player: ", player.ivsPlayer);
      console.log("Amazon IVS Player Events: ", player.ivsEvents);
      const PlayerEventType = player.ivsEvents;
      playerInstance.addEventListener(
        PlayerEventType.TEXT_METADATA_CUE,
        function (cue) {
          const metadataText = cue.text;
          const position = player.getPosition().toFixed(2);
          console.log(metadataText);
          //console.log(
          // `Player Event - TEXT_METADATA_CUE: "${metadataText}". Observed ${position}s after playback started.`
          //);
          //onsole.log(cue);
          //triggerQuiz(metadataText);
        }
      );
    });
  } catch (e) {
    console.error(e);
  }
  function triggerQuiz(metadataText) {
    let obj = JSON.parse(metadataText);

    quizEl.style.display = "";
    quizEl.classList.remove("drop");
    waitMessage.style.display = "none";
    cardInnerEl.style.display = "none";
    cardInnerEl.style.pointerEvents = "auto";

    while (answersEl.firstChild) answersEl.removeChild(answersEl.firstChild);
    questionEl.textContent = obj.question;

    let createAnswers = function (obj, i) {
      let q = document.createElement("a");
      let qText = document.createTextNode(obj.answers[i]);
      answersEl.appendChild(q);
      q.classList.add("answer");
      q.appendChild(qText);

      q.addEventListener("click", (event) => {
        cardInnerEl.style.pointerEvents = "none";
        if (q.textContent === obj.answers[obj.correctIndex]) {
          q.classList.toggle("correct");
        } else {
          q.classList.toggle("wrong");
        }
        setTimeout(function () {
          removeCard();
          waitMessage.style.display = "";
        }, 1050);
        return false;
      });
    };

    for (var i = 0; i < obj.answers.length; i++) {
      createAnswers(obj, i);
    }
    cardInnerEl.style.display = "";
  }
  waitMessage.style.display = "";
})(window.IVSPlayer);
/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. */
/* SPDX-License-Identifier: MIT-0 */

/* Reset */
*,*::before,*::after{box-sizing:border-box}ul[class],ol[class]{padding:0}body,h1,h2,h3,h4,p,ul[class],ol[class],figure,blockquote,dl,dd{margin:0}html{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}ul[class],ol[class]{list-style:none}a:not([class]){text-decoration-skip-ink:auto}img{max-width:100%;display:block}article>*+*{margin-top:1em}input,button,textarea,select{font:inherit}@media (prefers-reduced-motion:reduce){*{animation-duration:0.01ms!important;animation-iteration-count:1!important;transition-duration:0.01ms!important;scroll-behavior:auto!important}}

/* Variables */
:root {
  --radius: 12px;
}

/* Style */
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

body {
  overflow: hidden;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
  user-select: none;
}

#app {
  background: #334273;
  height: 100%;
}

.inner {
  max-width: 1080px;
  display: flex;
  flex-direction: column;
  position: relative;
  align-items: stretch;
  margin: 0 auto;
  padding: 40px;
}

.player-wrapper {
  width: 100%;
  position: relative;
  overflow: hidden;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
  border-radius: var(--radius);
  box-shadow: 0 6px 30px rgba(0, 0, 0, 0.3);
  z-index: 1;
}

.aspect-spacer {
  padding-bottom: 56.25%;
}

.el-player {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  background: #000;
  border-radius: var(--radius);
}

video {
  width: 100%;
  border-radius: var(--radius);
  background: #000;
}

.quiz-wrap {
  min-height: 460px;
  position: relative;
  transition: all 0.25s ease-in;
}

.card {
  margin: 0 20px;
  padding: 20px;
  position: absolute;
  left: 0;
  right: 0;
  background: #fff;
  border-radius: 20px;
  box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.1);
  transition: all 1s cubic-bezier(1, -0.56, 0, 1);
  transform: translate3d(0, 0, 0) scale(1);
  backface-visibility: hidden;
  z-index: 1;
}

.card.drop {
  opacity: 0;
  transform: translate3d(0, 200px, -20px) scale(0.92);
}

h2 {
  font-size: 25px;
  text-align: center;
  padding-bottom: 20px;
}

.answer {
  height: 50px;
  line-height: 50px;
  font-size: 20px;
  display: flex;
  text-decoration: none;
  border: 1px solid #d5dbdb;
  border-radius: 50px;
  padding: 0 24px;
  margin: 10px 0;
  background: #fafafa;
  color: #545b64;
  transition: all 0.05s ease-in-out;
}

.answer:hover {
  background: #ebebebe0;
}

.answer:active {
  background: #ff9900;
  border: 1px solid #eb5f07;
  color: #fff;
}

.answer.correct {
  background: #25a702;
  border: 1px solid #1d8102;
  color: #fff;
  animation: blink 0.45s infinite;
}

.answer.wrong {
  background: #d13212;
  border: 1px solid #b7290d;
  color: #fff;
  animation: blink 0.45s infinite;
}

#waiting {
  top: 100px;
  left: 0;
  right: 0;
  position: absolute;
  display: flex;
  align-items: center;
}

.waiting-text {
  width: 100%;
  display: block;
  text-align: center;
  font-size: 18px;
  color: #d5dbdb;
}

.float {
  transform: translateY(0px);
  animation: float 6s ease-in-out infinite;
}

/* Utility - Position */
.pos-absolute {
  position: absolute !important;
}
.pos-fixed {
  position: fixed !important;
}
.pos-relative {
  position: relative !important;
}
.top-0 {
  top: 0 !important;
}
.bottom-0 {
  bottom: 0 !important;
}

/* Utility - Width/Height */
.full-width {
  width: 100%;
}
.full-height {
  height: 100%;
}

/* Animations */
@keyframes blink {
  50% {
    opacity: 0.8;
  }
}

@keyframes float {
  0% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(-20px);
  }
  100% {
    transform: translateY(0px);
  }
}

/* Mediaqueries */
@media (max-width: 767px) {
  h2 {
    font-size: 20px;
  }
  .card {
    top: -20px;
  }
}

@media (min-width: 767px) {
  .card {
    top: -25%;
  }
}
  <head>
    <script src="https://content.jwplatform.com/libraries/oH2wJDod.js"></script>
    <script src="https://player.live-video.net/1.11.0/amazon-ivs-jw-provider.min.js"></script>
  </head>

  <body>
    <div id="app">
      <div class="inner">
        <!-- Player wrapper, forcing 16:9 aspect ratio -->
        <div class="player-wrapper">
          <div class="aspect-spacer"></div>
          <div class="pos-absolute full-width full-height top-0">
            <div id="video-player"></div>
          </div>
        </div>

        <!-- Quiz UI -->
        <div class="quiz-wrap">
          <div id="waiting">
            <span class="waiting-text float"
              >Waiting for the next question</span
            >
          </div>
          <div id="quiz" class="card drop">
            <div id="card-inner">
              <h2 id="question"></h2>
              <div id="answers"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <script src="script.js"></script>
  </body>
Lynob
  • 5,059
  • 15
  • 64
  • 114

1 Answers1

1

You are inside IIFE you can't declare outside of scope IIFE's are anonymous

Please read docume

const playbackUrl =
  "https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.xhP3ExfcX8ON.m3u8";
const ivsConfig = {
  playlist: [
    {
      file: playbackUrl,
      type: "ivs"
    }
  ]
};

const videoPlayer = document.getElementById("video-player");
const quizEl = document.getElementById("quiz");
const waitMessage = document.getElementById("waiting");
const questionEl = document.getElementById("question");
const answersEl = document.getElementById("answers");
const cardInnerEl = document.getElementById("card-inner");

(async (IVSPlayer) => {
  try {
    const playerInstance = jwplayer(videoPlayer).setup(ivsConfig);
    playerInstance.on("providerPlayer", function (player) {
      if (player) {
        const { ivsEvents, ivsPlayer } = player;
        ivsPlayer.addEventListener(
          ivsEvents.PlayerEventType.TEXT_METADATA_CUE,
          function (cue) {
            const metadataText = cue.text;
            // const position = player.getPosition().toFixed(2);
            // position is under state.
            const position = ivsPlayer.core.state.position.toFixed(2);
            console.log(
              `Player Event - TEXT_METADATA_CUE: "${metadataText}". Observed ${position}s after playback started.`
            );
            triggerQuiz(metadataText);
          }
        );
      }
    });
  } catch (e) {
    console.error(e);
  }
  function triggerQuiz(metadataText) {
    let obj = JSON.parse(metadataText);

    quizEl.style.display = "";
    quizEl.classList.remove("drop");
    waitMessage.style.display = "none";
    cardInnerEl.style.display = "none";
    cardInnerEl.style.pointerEvents = "auto";

    while (answersEl.firstChild) answersEl.removeChild(answersEl.firstChild);
    questionEl.textContent = obj.question;

    let createAnswers = function (obj, i) {
      let q = document.createElement("a");
      let qText = document.createTextNode(obj.answers[i]);
      answersEl.appendChild(q);
      q.classList.add("answer");
      q.appendChild(qText);

      q.addEventListener("click", (event) => {
        cardInnerEl.style.pointerEvents = "none";
        if (q.textContent === obj.answers[obj.correctIndex]) {
          q.classList.toggle("correct");
        } else {
          q.classList.toggle("wrong");
        }
        setTimeout(function () {
           // removeCard(); is not defined. you must
          //  create it first
          waitMessage.style.display = "";
        }, 1050);
        return false;
      });
    };

    for (var i = 0; i < obj.answers.length; i++) {
      createAnswers(obj, i);
    }
    cardInnerEl.style.display = "";
  }
  waitMessage.style.display = "";
})(window.IVSPlayer);
/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. */
/* SPDX-License-Identifier: MIT-0 */

/* Reset */
*,
*::before,
*::after {
  box-sizing: border-box;
}
ul[class],
ol[class] {
  padding: 0;
}
body,
h1,
h2,
h3,
h4,
p,
ul[class],
ol[class],
figure,
blockquote,
dl,
dd {
  margin: 0;
}
html {
  scroll-behavior: smooth;
}
body {
  min-height: 100vh;
  text-rendering: optimizeSpeed;
  line-height: 1.5;
}
ul[class],
ol[class] {
  list-style: none;
}
a:not([class]) {
  text-decoration-skip-ink: auto;
}
img {
  max-width: 100%;
  display: block;
}
article > * + * {
  margin-top: 1em;
}
input,
button,
textarea,
select {
  font: inherit;
}
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Variables */
:root {
  --radius: 12px;
}

/* Style */
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

body {
  overflow: hidden;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Ubuntu, "Helvetica Neue", sans-serif;
  user-select: none;
}

#app {
  background: #334273;
  height: 100%;
}

.inner {
  max-width: 1080px;
  display: flex;
  flex-direction: column;
  position: relative;
  align-items: stretch;
  margin: 0 auto;
  padding: 40px;
}

.player-wrapper {
  width: 100%;
  position: relative;
  overflow: hidden;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
  border-radius: var(--radius);
  box-shadow: 0 6px 30px rgba(0, 0, 0, 0.3);
  z-index: 1;
}

.aspect-spacer {
  padding-bottom: 56.25%;
}

.el-player {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  background: #000;
  border-radius: var(--radius);
}

video {
  width: 100%;
  border-radius: var(--radius);
  background: #000;
}

.quiz-wrap {
  min-height: 460px;
  position: relative;
  transition: all 0.25s ease-in;
}

.card {
  margin: 0 20px;
  padding: 20px;
  position: absolute;
  left: 0;
  right: 0;
  background: #fff;
  border-radius: 20px;
  box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.1);
  transition: all 1s cubic-bezier(1, -0.56, 0, 1);
  transform: translate3d(0, 0, 0) scale(1);
  backface-visibility: hidden;
  z-index: 1;
}

.card.drop {
  opacity: 0;
  transform: translate3d(0, 200px, -20px) scale(0.92);
}

h2 {
  font-size: 25px;
  text-align: center;
  padding-bottom: 20px;
}

.answer {
  height: 50px;
  line-height: 50px;
  font-size: 20px;
  display: flex;
  text-decoration: none;
  border: 1px solid #d5dbdb;
  border-radius: 50px;
  padding: 0 24px;
  margin: 10px 0;
  background: #fafafa;
  color: #545b64;
  transition: all 0.05s ease-in-out;
}

.answer:hover {
  background: #ebebebe0;
}

.answer:active {
  background: #ff9900;
  border: 1px solid #eb5f07;
  color: #fff;
}

.answer.correct {
  background: #25a702;
  border: 1px solid #1d8102;
  color: #fff;
  animation: blink 0.45s infinite;
}

.answer.wrong {
  background: #d13212;
  border: 1px solid #b7290d;
  color: #fff;
  animation: blink 0.45s infinite;
}

#waiting {
  top: 100px;
  left: 0;
  right: 0;
  position: absolute;
  display: flex;
  align-items: center;
}

.waiting-text {
  width: 100%;
  display: block;
  text-align: center;
  font-size: 18px;
  color: #d5dbdb;
}

.float {
  transform: translateY(0px);
  animation: float 6s ease-in-out infinite;
}

/* Utility - Position */
.pos-absolute {
  position: absolute !important;
}
.pos-fixed {
  position: fixed !important;
}
.pos-relative {
  position: relative !important;
}
.top-0 {
  top: 0 !important;
}
.bottom-0 {
  bottom: 0 !important;
}

/* Utility - Width/Height */
.full-width {
  width: 100%;
}
.full-height {
  height: 100%;
}

/* Animations */
@keyframes blink {
  50% {
    opacity: 0.8;
  }
}

@keyframes float {
  0% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(-20px);
  }
  100% {
    transform: translateY(0px);
  }
}

/* Mediaqueries */
@media (max-width: 767px) {
  h2 {
    font-size: 20px;
  }
  .card {
    top: -20px;
  }
}

@media (min-width: 767px) {
  .card {
    top: -25%;
  }
}
<head>
  <script src="https://content.jwplatform.com/libraries/oH2wJDod.js"></script>
  <script src="https://player.live-video.net/1.11.0/amazon-ivs-jw-provider.min.js"></script>
</head>

<body>
  <div id="app">
    <div class="inner">
      <!-- Player wrapper, forcing 16:9 aspect ratio -->
      <div class="player-wrapper">
        <div class="aspect-spacer"></div>
        <div class="pos-absolute full-width full-height top-0">
          <div id="video-player"></div>
        </div>
      </div>

      <!-- Quiz UI -->
      <div class="quiz-wrap">
        <div id="waiting">
          <span class="waiting-text float">Waiting for the next question</span>
        </div>
        <div id="quiz" class="card drop">
          <div id="card-inner">
            <h2 id="question"></h2>
            <div id="answers"></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
  • @Lynob code updated! – Ploutarchos Michaelides Jul 26 '22 at 11:58
  • I edited my question to include a code snippet at the end, now I get this error ``Error in "providerPlayer" event handler: TypeError: playerInstance.addEventListener is not a function` and this was my initial question, I unable to add event listeners to jw player – Lynob Jul 26 '22 at 13:03
  • why you are trying to attach event listener on the player instance ?? Did you read documentation?? [codepen](https://codepen.io/ploutarchosm/pen/qBoXwNK) – Ploutarchos Michaelides Jul 26 '22 at 14:27
  • Thank you so much, this works! was trying to attach event listeners because the official [demo](https://codepen.io/amazon-ivs/pen/XWmjEKN) has event listeners and the [official documentation](https://docs.aws.amazon.com/ivs/latest/userguide/player-jwplayer.html) says to use event listeners as well, I thought I would need to create a callback function to get the meta data, I also tried [onMeta](https://developer.jwplayer.com/jwplayer/docs/jw8-javascript-api-reference#metadata), you didn't use any of that – Lynob Jul 26 '22 at 16:06
  • by the way in the codepen, there's a very minor issue, `const position = player.getPosition().toFixed(2);` undefined, I'll be looking into why, if you happen to know why, please let me know, if not that's okay, a minor bug that I can fix – Lynob Jul 26 '22 at 16:08
  • @Lynob to be honest doc is confusing... I will have a look later for the the rest and I will let you know. :) – Ploutarchos Michaelides Jul 27 '22 at 06:19