Skip to content

Instantly share code, notes, and snippets.

@hpaul
Created April 29, 2026 16:14
Show Gist options
  • Select an option

  • Save hpaul/d088f035bf8025772d6ffcf1cb084d2e to your computer and use it in GitHub Desktop.

Select an option

Save hpaul/d088f035bf8025772d6ffcf1cb084d2e to your computer and use it in GitHub Desktop.
Wistia Aurora player usage
import { WistiaPlayer as OriginalWistiaPlayer } from '@wistia/wistia-player';
import { WistiaPlayer } from '@wistia/wistia-player-react';
const VideoModa = ({ videoId, videoReference: threePlayVideoId, ...props }) => {
const {
params,
location: { query },
} = props.router || {};
const { apd } = query;
const { subject: subjectId } = params || {};
const [
sendTelemetryIntermediatePlayEvent,
setSendTelemetryIntermediatePlayEvent,
] = useState(false);
const player = useRef<HTMLElement>(null);
const video = useRef();
const interval = useRef<ReturnType<typeof setInterval>>();
const saveDelayCounter = useRef(0);
const updateSaveDelayCounter = (payload: number) => {
saveDelayCounter.current = payload;
};
const { onPlay, onPlaying, onPause, onSeeked, onEnd, onClose } =
eventCallbacks;
const progress = useRef<number[]>([]);
const containerRef = useRef<HTMLDivElement>(null);
const getProgress = () => {
if (player?.current) {
let currentProgress = player?.current?.secondsWatchedVector;
let previousProgress = progressVector;
const duration = player?.current?.duration;
const getEmptyProgress = (): number[] => {
return Array.from({ length: duration + 1 }, () => 0);
};
if (previousProgress.length === 0) {
previousProgress = getEmptyProgress();
}
if (currentProgress.length === 0) {
currentProgress = getEmptyProgress();
}
return mergeProgress(previousProgress, currentProgress);
} else {
return progress?.current;
}
};
const updateProgress = () => {
progress.current = getProgress();
};
const latestVideoPosition = useRef('0.00');
const getLatestVideoPosition = () => {
if (player?.current) {
const truncateNumber = (num: number): number =>
Math.trunc(num * 1000) / 1000;
const currentTime = player?.current?.currentTime;
const duration = player?.current?.duration;
const percentWatched = currentTime / duration;
const latestVideoPosition = truncateNumber(percentWatched).toString();
return latestVideoPosition;
} else {
return latestVideoPosition?.current;
}
};
const updateLatestVideoPosition = () => {
latestVideoPosition.current = getLatestVideoPosition();
};
const getProgressInfo = (): ProgressInfo => ({
progress: getProgress(),
latestVideoPosition: getLatestVideoPosition(),
});
const handlePlay = () => {
const progressInfo = getProgressInfo();
sendTelemetryEvent('play', progressInfo);
createInterval();
onPlay(progressInfo);
};
const handleEnd = () => {
const progressInfo = getProgressInfo();
sendTelemetryEvent('end', progressInfo);
deleteInterval();
onEnd(progressInfo);
};
const handlePause = () => {
const progressInfo = getProgressInfo();
sendTelemetryEvent('stop', progressInfo);
deleteInterval();
onPause(progressInfo);
};
const seekedTimer = useRef<ReturnType<typeof setTimeout>>();
const handleSeeked = () => {
clearTimeout(seekedTimer.current);
// Seeking should not trigger one call per each second (100s seeked -> 100req)
// Applying 300ms is enough to debounce and persist the intention
seekedTimer.current = setTimeout(() => {
const progressInfo = getProgressInfo();
sendTelemetryEvent('seeked', progressInfo);
onSeeked(progressInfo);
}, 300);
};
const handlePlaying = () => {
if (!player?.current) return;
const newSecondsHaveElapsed = saveDelayCounter?.current >= 14;
if (newSecondsHaveElapsed) {
const progressInfo = getProgressInfo();
sendTelemetryEvent('playing', progressInfo);
onPlaying(false, newSecondsHaveElapsed, progressInfo);
updateSaveDelayCounter(0);
}
};
const handleClose = () => {
const progressInfo = getProgressInfo();
sendTelemetryEvent('close', progressInfo);
deleteInterval();
onClose(progressInfo);
if (isImpersonating && apdCarouselData?.[subjectId]) {
setAPDCarouselData({ subject: subjectId, videoData: null });
}
};
useEffect(() => handleClose, []);
const onSecondChange = () => {
updateProgress();
updateLatestVideoPosition();
updateSaveDelayCounter(saveDelayCounter?.current + 1);
};
const [isVideoPreloaded, setIsVideoPreloaded] = useState(false);
const {
me: { initId: userId, sections },
chosenEducationPeriodCode,
} = userData;
const createInterval = () => {
interval.current = setInterval(
() => setSendTelemetryIntermediatePlayEvent(true),
15000,
);
};
const deleteInterval = () => {
if (interval.current) {
clearInterval(interval.current);
setSendTelemetryIntermediatePlayEvent(false);
}
};
const onVideoPreloaded = () => setIsVideoPreloaded(true);
// Send telemetry event whenever the `sendTelemetryIntermediatePlayEvent` is `true`.
useEffect(() => {
if (sendTelemetryIntermediatePlayEvent) {
const progressInfo = getProgressInfo();
sendTelemetryEvent('play', progressInfo);
setSendTelemetryIntermediatePlayEvent(false);
}
}, [sendTelemetryIntermediatePlayEvent]);
const initialCurrentTime = useMemo(() => {
const duration = progressVector.length;
return duration * Number(videoPosition);
}, [videoPosition, progressVector]);
const videoContainerId = `video_container_${videoId}`;
return (
<div ref={containerRef}>
{!isVideoPreloaded && <LoadingSpinner containerClassName="flex" />}
<WistiaPlayer
id={videoContainerId}
swatch
resumable={false}
currentTime={initialCurrentTime}
mediaId={videoId}
ref={player}
onSwatchLoaded={onVideoPreloaded}
onPlay={handlePlay}
onPause={handlePause}
onSeeked={handleSeeked}
onEnded={handleEnd}
onPercentWatchedChange={handlePlaying}
onSecondChange={onSecondChange}
/>
<ThreePlayPlugin
target={videoContainerId}
videoId={videoId}
threePlayVideoId={threePlayVideoId}
/>
</div>
);
};
export default VideoModal;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment