diff --git a/backend/db.js b/backend/db.js
index 43a71a2..4eaf108 100644
--- a/backend/db.js
+++ b/backend/db.js
@@ -1,4 +1,4 @@
-var { callModel: Call } = require("./models/call");
+var Call = require("./models/call");
var Event = require("./models/event");
var Podcast = require("./models/podcast");
diff --git a/frontend/src/Call/components/MediaPlayer.js b/frontend/src/Call/components/MediaPlayer.js
index 681d938..912aada 100644
--- a/frontend/src/Call/components/MediaPlayer.js
+++ b/frontend/src/Call/components/MediaPlayer.js
@@ -11,6 +11,7 @@ import {
Button
} from "semantic-ui-react";
import ReactAudioPlayer from 'react-audio-player'
+import { useWavesurfer } from './WaveSurfer' //'@wavesurfer/react'
import WavesurferPlayer from '@wavesurfer/react'
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.js';
@@ -22,21 +23,55 @@ import "./MediaPlayer.css";
const MediaPlayer = (props) => {
+ const containerRef = useRef(null)
const audioRef = React.createRef();
const call = props.call;
const [sourceIndex, setSourceIndex] = useState(0);
- const [wavesurfer, setWavesurfer] = useState(null)
- const [isPlaying, setIsPlaying] = useState(false)
+ //const [wavesurfer, setWavesurfer] = useState(null)
+ //const [isPlaying, setIsPlaying] = useState(false)
const [playTime, setPlayTime] = useState(0);
const playSilence = props.playSilence;
const parentHandlePlayPause = props.onPlayPause
const regionsPlugin = useMemo(() => RegionsPlugin.create(), []);
const plugins = useMemo(() => [regionsPlugin], [regionsPlugin]);
+ //
+ const { wavesurfer, isReady, isPlaying, currentTime, hasFinished } = useWavesurfer({
+ container: containerRef,
+ waveColor: "#E81B39",
+ height: 25,
+ barWidth: 3,
+ barGap: 3,
+ barRadius: 6,
+ plugins: plugins,
+ backend: 'MediaElementWebAudio',
+ responsive: true,
+ //autoplay: true,
+ })
+
+ // useEffect(() => {
+ // if (wavesurfer) {
+ // wavesurfer.on('finish', props.onEnded);
+ // }
+ // }, [wavesurfer])
useEffect(() => {
setSourceIndex(0);
-
+
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: "Waiting for Call...",
@@ -68,53 +103,50 @@ const MediaPlayer = (props) => {
}, [playSilence]);
useEffect(() => {
+ if (hasFinished) {
+ props.onEnded();
+ }
+ }, [hasFinished]);
+
+ useEffect(() => {
+ if (call && wavesurfer) {
+ wavesurfer.load(call.url);
+ }
setSourceIndex(0);
}, [call]);
+ useEffect(() => {
- const onReady = (ws) => {
- setWavesurfer(ws)
- setIsPlaying(false)
- regionsPlugin.clearRegions();
- if (call) {
- call.srcList.forEach(src => {
- regionsPlugin.addRegion({
- start: src.pos,
- color: "rgba(128, 128, 128, 1.0)",
- drag: false,
- resize: false
- });
- });
- }
- }
-
- const onPlay = () => {
- setIsPlaying(true);
- parentHandlePlayPause(true);
-
- }
-
- const onPause = () => {
- setIsPlaying(false);
- parentHandlePlayPause(false);
- }
- const onPlayPause = () => {
- wavesurfer && wavesurfer.playPause()
- }
-
- const updatePlayProgress = () => {
-
- if (wavesurfer && wavesurfer.isPlaying()) {
- var totalTime = wavesurfer.getDuration(),
- currentTime = wavesurfer.getCurrentTime(),
- remainingTime = totalTime - currentTime;
-
- if (!isPlaying) {
- setIsPlaying(true);
+ if (isReady && wavesurfer) {
+ wavesurfer.play()
+ //regionsPlugin.clearRegions();
+ if (call) {
+ // call.srcList.forEach(src => {
+ // regionsPlugin.addRegion({
+ // start: src.pos,
+ // color: "rgba(128, 128, 128, 1.0)",
+ // drag: false,
+ // resize: false
+ // });
+ // });
}
- //console.log("totalTime: " + totalTime + " currentTime: " + currentTime + " remainingTime: " + remainingTime);
+ }
+ }, [isReady]);
+
+ useEffect(() => {
+ if (isPlaying) {
+ parentHandlePlayPause(true);
+ } else {
+ parentHandlePlayPause(false);
+ }
+ }, [isPlaying]);
+
+ useEffect(() => {
+ if (wavesurfer && wavesurfer.isPlaying()) {
+ //console.log("totalTime: " + totalTime + " currentTime: " + currentTime + " remainingTime: " + remainingTime);
+
// this checks to see if it should display the next Source ID
if (call && ((call.srcList.length - 1) >= (sourceIndex + 1)) && (currentTime > call.srcList[sourceIndex + 1].pos)) {
setSourceIndex(sourceIndex + 1);
@@ -123,6 +155,10 @@ const MediaPlayer = (props) => {
setPlayTime(Math.floor(currentTime));
}
+ }, [currentTime]);
+
+ const onPlayPause = () => {
+ wavesurfer && wavesurfer.playPause()
}
let playEnabled = { "disabled": true }
@@ -143,29 +179,15 @@ const MediaPlayer = (props) => {
- {
- isPlaying
- ? ()
- : ()
- }
+ {
+ isPlaying
+ ? ()
+ : ()
+ }
diff --git a/frontend/src/Call/components/WaveSurfer.js b/frontend/src/Call/components/WaveSurfer.js
new file mode 100644
index 0000000..4232c5d
--- /dev/null
+++ b/frontend/src/Call/components/WaveSurfer.js
@@ -0,0 +1,200 @@
+/**
+ * A React component for wavesurfer.js
+ *
+ * Usage:
+ *
+ * import WavesurferPlayer from '@wavesurfer/react'
+ *
+ *
console.log('Ready!', wavesurfer)}
+ * />
+ */
+
+import { set } from 'date-fns'
+import { useState, useMemo, useEffect, useRef, memo } from 'react'
+import WaveSurfer from 'wavesurfer.js'
+
+/**
+ * Use wavesurfer instance
+ */
+function useWavesurferInstance(containerRef, options) {
+ const [wavesurfer, setWavesurfer] = useState(null)
+ // Flatten options object to an array of keys and values to compare them deeply in the hook deps
+ const flatOptions = useMemo(() => Object.entries(options).flat(), [options])
+
+ // Create a wavesurfer instance
+ useEffect(() => {
+ if (!containerRef.current) return
+
+ const ws = WaveSurfer.create({
+ ...options,
+ container: containerRef.current,
+ })
+
+ setWavesurfer(ws)
+
+ return () => {
+ ws.destroy()
+ }
+ }, [containerRef, ...flatOptions])
+
+ return wavesurfer
+}
+
+/**
+ * Use wavesurfer state
+ */
+function useWavesurferState(wavesurfer) {
+ const [isReady, setIsReady] = useState(false)
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [currentTime, setCurrentTime] = useState(0)
+ const [hasFinished, setHasFinished] = useState(false)
+
+ useEffect(() => {
+ if (!wavesurfer) return
+
+ const unsubscribeFns = [
+ wavesurfer.on('load', () => {
+ setIsReady(false)
+ setIsPlaying(false)
+ setCurrentTime(0)
+ }),
+
+ wavesurfer.on('ready', () => {
+ setIsReady(true)
+ setIsPlaying(false)
+ setHasFinished(false)
+ setCurrentTime(0)
+ }),
+
+ wavesurfer.on('finish', () => {
+ setHasFinished(true)
+ }),
+
+ wavesurfer.on('play', () => {
+ setIsPlaying(true)
+ }),
+
+ wavesurfer.on('pause', () => {
+ setIsPlaying(false)
+ }),
+
+ wavesurfer.on('timeupdate', () => {
+ setCurrentTime(wavesurfer.getCurrentTime())
+ }),
+
+ wavesurfer.on('destroy', () => {
+ setIsReady(false)
+ setIsPlaying(false)
+ }),
+ ]
+
+ return () => {
+ unsubscribeFns.forEach((fn) => fn())
+ }
+ }, [wavesurfer])
+
+ return useMemo(
+ () => ({
+ isReady,
+ isPlaying,
+ currentTime,
+ hasFinished
+ }),
+ [isPlaying, currentTime, isReady, hasFinished],
+ )
+}
+
+const EVENT_PROP_RE = /^on([A-Z])/
+const isEventProp = (key) => EVENT_PROP_RE.test(key)
+const getEventName = (key) => key.replace(EVENT_PROP_RE, (_, $1) => $1.toLowerCase())
+
+/**
+ * Parse props into wavesurfer options and events
+ */
+function useWavesurferProps(props) {
+ // Props starting with `on` are wavesurfer events, e.g. `onReady`
+ // The rest of the props are wavesurfer options
+ return useMemo(() => {
+ const allOptions = { ...props }
+ const allEvents = { ...props }
+
+ for (const key in allOptions) {
+ if (isEventProp(key)) {
+ delete allOptions[key]
+ } else {
+ delete allEvents[key]
+ }
+ }
+ return [allOptions, allEvents]
+ }, [props])
+}
+
+/**
+ * Subscribe to wavesurfer events
+ */
+function useWavesurferEvents(wavesurfer, events) {
+ const flatEvents = useMemo(() => Object.entries(events).flat(), [events])
+
+ // Subscribe to events
+ useEffect(() => {
+ if (!wavesurfer) return
+
+ const eventEntries = Object.entries(events)
+ if (!eventEntries.length) return
+
+ const unsubscribeFns = eventEntries.map(([name, handler]) => {
+ const event = getEventName(name)
+ return wavesurfer.on(event, (...args) => handler(wavesurfer, ...args))
+ })
+
+ return () => {
+ unsubscribeFns.forEach((fn) => fn())
+ }
+ }, [wavesurfer, ...flatEvents])
+}
+
+/**
+ * Wavesurfer player component
+ * @see https://wavesurfer.xyz/docs/modules/wavesurfer
+ */
+const WavesurferPlayer = memo((props) => {
+ const containerRef = useRef(null)
+ const [options, events] = useWavesurferProps(props)
+ const wavesurfer = useWavesurferInstance(containerRef, options)
+ useWavesurferEvents(wavesurfer, events)
+
+ // Create a container div
+ return
+})
+
+export default WavesurferPlayer
+
+/**
+ * React hook for wavesurfer.js
+ *
+ * ```
+ * import { useWavesurfer } from '@wavesurfer/react'
+ *
+ * const App = () => {
+ * const containerRef = useRef(null)
+ *
+ * const { wavesurfer, isReady, isPlaying, currentTime } = useWavesurfer({
+ * container: containerRef,
+ * url: '/my-server/audio.ogg',
+ * waveColor: 'purple',
+ * height: 100',
+ * })
+ *
+ * return
+ * }
+ * ```
+ */
+export function useWavesurfer({ container, ...options }) {
+ const wavesurfer = useWavesurferInstance(container, options)
+ const state = useWavesurferState(wavesurfer)
+ return useMemo(() => ({ ...state, wavesurfer }), [state, wavesurfer])
+}
\ No newline at end of file