Jump to content

Como fazer a implementação do SCORM com react.js?


Yuri Ruan

Postagens Recomendadas

Nesse arquivo implemento a leitura do SCORM com a lib pipwerks, e com o iframe renderizo o conteúdo WBT, porém me deparo com esse erro:

 Error grabbing 1.2 API-SecurityError:Failed to read a named property 'API_1484_11' from 'Window': Blocked a frame with origin "https://........." from accessing a cross-origin frame.
36:Sat Sep 07 2024 09:42:17 GMT-0300 (Horário Padrão de Brasília) - Unable to acquire SCORM API:
37:Sat Sep 07 2024 09:42:17 GMT-0300 (Horário Padrão de Brasília) - SCORM2004_objAPI=object
38:Sat Sep 07 2024 09:42:17 GMT-0300 (Horário Padrão de Brasília) - In InitializeExecuted, blnSuccess=false, strErrorMessage=Error - unable to acquire LMS API, content may not play properly and results may not be recorded. Please contact technical support.
39:Sat Sep 07 2024 09:42:17 GMT-0300 (Horário Padrão de Brasília) - ERROR - LMS Initialize Failed
40:Sat Sep 07 2024 09:42:17 GMT-0300 (Horário Padrão de Brasília) - In DisplayError, strMessage=Error - unable to acquire LMS API, content may not play properly and results may not be recorded. Please contact technical support.
 

Código:

/* eslint-disable consistent-return */
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable import/extensions */

import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useCourseDetails } from '@context';
import {
  LessonVideoProgressBody,
  ListUserCourseResponse,
  updateLessonVideoProgress,
} from '@api';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { http } from '@services';
import pipwerks from 'pipwerks-scorm-api-wrapper';
import { Box, Icon } from '@/components';
import { PlayerEventListener } from '@/components/display/VideoPlayer/interface';
import {
  CustomPlayer,
  FullScreenButton,
  IframeContainer,
  VideoIFrame,
  WaitingScreenContainer,
  WaitingScreenDescription,
  WaitingScreenIcon,
  WaitingScreenTitle,
} from './styles';
import { SecurityOverlay } from '@/components/feedback/SecurityOverlay';

function VideoPlayerContent() {
  const {
    currentTimer,
    lesson,
    refetchCourseDetails,
    setCourseProgress,
    courseId,
  } = useCourseDetails();

  const iframeRef = useRef<HTMLIFrameElement>(null);
  const timerRef = useRef<number | undefined>(Number(lesson?.pauseAt));
  const [isFullScreenButtonVisible, setIsFullScreenButtonVisible] = useState(true);
  let timeoutId: NodeJS.Timeout;

  useEffect(() => {
    if (isFullScreenButtonVisible) {
      timeoutId = setTimeout(() => {
        setIsFullScreenButtonVisible(false);
      }, 2000);
    }
    return () => timeoutId && clearTimeout(timeoutId);
  }, []);

  const onMouseEnter = () => {
    clearTimeout(timeoutId);
    setIsFullScreenButtonVisible(true);
  };

  const onMouseLeave = () => {
    if (isFullScreenButtonVisible) {
      timeoutId = setTimeout(() => {
        setIsFullScreenButtonVisible(false);
      }, 2000);
    }
  };

  const queryClient = useQueryClient();
  useEffect(() => {
    if (lesson?.contentType === 'WBT') {
      (async () => {
        const getCMIroute = `/course/lesson/${lesson.id}/cmi`;
        console.log('Lesson ID:', lesson?.id);
        const strObjCMI = await http.get(getCMIroute);
        const strCMI = strObjCMI.data.cmi;
        console.log('strCMI: ', strCMI);

        const api = pipwerks.SCORM.API.find(window);
        if (api) {
          console.log('SCORM API encontrada:', api);
          pipwerks.SCORM.init();

          if (strCMI) {
            const jsonCMI = JSON.parse(strCMI);
            pipwerks.SCORM.set('cmi.core.student_name', jsonCMI.studentName);
          }

          pipwerks.SCORM.save = async () => {
            const postCMIroute = `/course/lesson/${lesson.id}/cmi`;
            const cmi = JSON.stringify(
              pipwerks.SCORM.get('cmi.core.lesson_status')
            );
            const response = await http.post(postCMIroute, { cmi });
            if (response.data) {
              if (
                'certificateAuthenticationId' in response.data &&
                'progress' in response.data
              ) {
                const { progress, certificateAuthenticationId } = response.data;
                setCourseProgress(progress);

                queryClient.setQueryData<{ data: ListUserCourseResponse }>(
                  ['listCourseDetails', courseId],
                  (dadosAntigos: any) => {
                    const updatedData = {
                      ...dadosAntigos,
                      data: {
                        ...dadosAntigos!.data,
                        certificateAuthenticationId,
                      },
                    };
                    return updatedData;
                  }
                );

                refetchCourseDetails();
              }
            }
          };
        } else {
          console.error('SCORM API não encontrada no contexto da janela.');
        }
      })();
      return () => {
        pipwerks.SCORM.quit();
      };
    }
  }, [lesson, pipwerks]);

  const { mutate } = useMutation({
    mutationFn: updateLessonVideoProgress,
  });

  const updateVideoProgress = useCallback(
    (body: LessonVideoProgressBody) => {
      const data = {
        id: lesson?.id,
        body,
      };
      mutate(data);
    },
    [lesson?.id, mutate]
  );

  const getEventListeners = (player: PlayerEventListener) => {
    switch (player.event) {
      case 'onProgress':
        if (typeof player.eventParam === 'number') {
          timerRef.current = Number(player.eventParam?.toFixed(6));
        }
        break;
      case 'onStart':
        updateVideoProgress({
          smbVideosMediaId: lesson?.smbVideosMediaId,
        });
        break;
      case 'onPause':
      case 'onCuepoint':
        if (
          typeof player.eventParam === 'object' &&
          player.duration === player.eventParam.time
        ) {
          refetchCourseDetails();
        }
        updateVideoProgress({
          smbVideosMediaId: lesson?.smbVideosMediaId,
          pauseAt: Number(timerRef.current),
        });
        break;
      default:
        break;
    }
  };

  const enterFullScreen = () => {
    const iframe = iframeRef.current;
    if (iframe) {
      if (iframe.requestFullscreen) {
        iframe.requestFullscreen();
      }
    }
  };

  if (
    import.meta.env.VITE_REACT_APP_ENV === 'production' &&
    lesson?.contentType !== 'WBT' &&
    lesson?.smbVideosMediaStatus === 'EMPTY'
  ) {
    return (
      <Box>
        <WaitingScreenContainer>
          <WaitingScreenIcon name="AccessTime" size="xxl" variant="secondary" />
          <WaitingScreenTitle variant="h2">
            Aguarde o processamento da aula
          </WaitingScreenTitle>
          <WaitingScreenDescription variant="h6">
            O arquivo cadastrado está sendo processado pelo nosso sistema, em
            instantes estará disponível para visualização.
          </WaitingScreenDescription>
        </WaitingScreenContainer>
      </Box>
    );
  }

  if (lesson?.contentType === 'WBT') {
    return (
      <Box onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
        {isFullScreenButtonVisible && (
          <FullScreenButton variant="contained" onClick={enterFullScreen}>
            <Icon name="ZoomOutMap" variant="white" />
          </FullScreenButton>
        )}
        <IframeContainer ref={iframeRef}>
          <VideoIFrame
            id="embeded-scorm"
            src={lesson?.file}
            title={lesson?.title}
            allow="geolocation; microphone; camera; encrypted-media; midi"
          />
          <SecurityOverlay />
        </IframeContainer>
      </Box>
    );
  }

  return (
    <CustomPlayer
      {...{ getEventListeners }}
      playerHash="56085afe3398157772a2bce2d2cd6606"
      resume={currentTimer ?? false}
      hasOverlay
      midiaId={`${lesson?.smbVideosMediaId}`}
      duration={lesson?.smbVideosMediaDuration}
    />
  );
}

export default memo(VideoPlayerContent);

 

O erro pode está realmente relacionado a forma de montar o componente ou pode ser falha do back? 

Link to comment
Compartilhe em outros sites

Crie uma conta ou entre para comentar 😀

Você precisa ser um membro para deixar um comentário.

Crie a sua conta

Participe da nossa comunidade, crie sua conta.
É bem rápido!

Criar minha conta agora

Entrar

Você já tem uma conta?
Faça o login agora.

Entrar agora


×
×
  • Create New...