Skip to content

Commit c27634e

Browse files
authored
Merge pull request #2 from adrmac/playbar
Playbar and Continuous Play
2 parents 8b40941 + fe78ea9 commit c27634e

File tree

14 files changed

+462
-110
lines changed

14 files changed

+462
-110
lines changed

ui/src/components/CandidateCard.tsx

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Theme } from "@mui/material";
22
import {
33
Box,
4+
Button,
45
Card,
56
CardActionArea,
67
CardContent,
@@ -9,13 +10,12 @@ import {
910
Typography,
1011
} from "@mui/material";
1112
import { useMediaQuery } from "@mui/material";
12-
import { MutableRefObject, useCallback } from "react";
13+
import { MutableRefObject } from "react";
1314

15+
import { useNowPlaying } from "@/context/NowPlayingContext";
1416
import { Feed } from "@/graphql/generated";
1517
import { Candidate } from "@/types/DataTypes";
1618

17-
import { CandidateCardAIPlayer } from "./Player/CandidateCardAIPlayer";
18-
import { CandidateCardPlayer } from "./Player/CandidateCardPlayer";
1919
import { VideoJSPlayer } from "./Player/VideoJS";
2020

2121
const tagRegex = [
@@ -40,9 +40,11 @@ export default function CandidateCard(props: {
4040
// changeListState?: (value: number, status: string) => void;
4141
// command?: string;
4242
players: MutableRefObject<{ [index: number]: VideoJSPlayer }>;
43-
playNext: boolean;
43+
// playNext: boolean;
4444
feeds: Feed[];
4545
}) {
46+
const { nowPlaying, setNowPlaying, masterPlayerRef, masterPlayerStatus } =
47+
useNowPlaying();
4648
const lgUp = useMediaQuery((theme: Theme) => theme.breakpoints.up("lg"));
4749

4850
const { descriptions } = props.candidate;
@@ -63,49 +65,65 @@ export default function CandidateCard(props: {
6365
const lastTimestamp = lastCandidate.timestamp;
6466
const firstTimestampString = firstCandidate.timestampString;
6567
const lastTimestampString = lastCandidate.timestampString;
66-
const feed =
67-
props.feeds.find((feed) => feed.id === firstCandidate.feedId) ||
68-
props.feeds.find((feed) => feed.id === lastCandidate.feedId);
68+
// const feed =
69+
// props.feeds.find((feed) => feed.id === firstCandidate.feedId) ||
70+
// props.feeds.find((feed) => feed.id === lastCandidate.feedId);
6971

70-
const humanReports = candidateArray.filter(
71-
(d) => d.playlistTimestamp !== undefined && d.playerOffset !== undefined,
72-
);
72+
// const humanReports = candidateArray.filter(
73+
// (d) => d.playlistTimestamp !== undefined && d.playerOffset !== undefined,
74+
// );
7375

74-
const startPlaylistTimestamp = Math.min(
75-
...humanReports.map((d) => +d.playlistTimestamp),
76-
);
76+
// const startPlaylistTimestamp = Math.min(
77+
// ...humanReports.map((d) => +d.playlistTimestamp),
78+
// );
7779

78-
const offsetPadding = 15;
79-
const minOffset = Math.min(...humanReports.map((d) => +d.playerOffset));
80+
// const offsetPadding = 15;
81+
// const minOffset = Math.min(...humanReports.map((d) => +d.playerOffset));
8082

81-
// const maxOffset = Math.max(...candidateArray.map((d) => +d.playerOffset));
82-
// instead, ensure that the last offset is still in the same playlist -- future iteration may pull a second playlist if needed
83-
const firstPlaylist = humanReports.filter(
84-
(d) => +d.playlistTimestamp === startPlaylistTimestamp,
85-
);
83+
// // const maxOffset = Math.max(...candidateArray.map((d) => +d.playerOffset));
84+
// // instead, ensure that the last offset is still in the same playlist -- future iteration may pull a second playlist if needed
85+
// const firstPlaylist = humanReports.filter(
86+
// (d) => +d.playlistTimestamp === startPlaylistTimestamp,
87+
// );
8688

87-
const maxOffset = Math.max(...firstPlaylist.map((d) => +d.playerOffset));
88-
const startOffset = Math.max(0, minOffset - offsetPadding);
89-
const endOffset = maxOffset + offsetPadding;
90-
91-
const onPlay = useCallback(() => {
92-
Object.entries(props.players.current).forEach(([key, player]) => {
93-
if (+key !== props.index) {
94-
player.pause();
95-
}
96-
});
97-
}, [props.index, props.players]);
98-
99-
const onPlayerEnd = useCallback(() => {
100-
if (props.playNext) props.players.current[props.index + 1]?.play();
101-
}, [props.playNext, props.index, props.players]);
102-
103-
const onPlayerInit = useCallback(
104-
(player: VideoJSPlayer) => {
105-
props.players.current[props.index] = player;
106-
},
107-
[props.index, props.players],
108-
);
89+
// const maxOffset = Math.max(...firstPlaylist.map((d) => +d.playerOffset));
90+
// const startOffset = Math.max(0, minOffset - offsetPadding);
91+
// const endOffset = maxOffset + offsetPadding;
92+
93+
// const onPlay = useCallback(() => {
94+
// Object.entries(props.players.current).forEach(([key, player]) => {
95+
// if (+key !== props.index) {
96+
// player.pause();
97+
// }
98+
// });
99+
// }, [props.index, props.players]);
100+
101+
// const onPlayerEnd = useCallback(() => {
102+
// if (props.playNext) props.players.current[props.index + 1]?.play();
103+
// }, [props.playNext, props.index, props.players]);
104+
105+
// const onPlayerInit = useCallback(
106+
// (player: VideoJSPlayer) => {
107+
// props.players.current[props.index] = player;
108+
// },
109+
// [props.index, props.players],
110+
// );
111+
112+
const handlePlay = (candidate: Candidate) => {
113+
console.log("clicked, candidate is", JSON.stringify(candidate));
114+
setNowPlaying(candidate);
115+
masterPlayerRef?.current?.play();
116+
};
117+
118+
const handlePause = () => {
119+
masterPlayerRef?.current?.pause();
120+
};
121+
122+
// useEffect(() => {
123+
// console.log("nowPlaying is ", JSON.stringify(nowPlaying));
124+
// console.log("candidate is ", JSON.stringify(candidate));
125+
// console.log("candidate === nowPlaying: ", candidate === nowPlaying);
126+
// }, [nowPlaying, candidate]);
109127

110128
return (
111129
<Card
@@ -127,8 +145,15 @@ export default function CandidateCard(props: {
127145
minWidth: lgUp ? 250 : 0,
128146
}}
129147
>
130-
{feed && candidate.array[0].playlistTimestamp ? (
131-
<CandidateCardPlayer
148+
{candidate !== nowPlaying ? (
149+
<Button onClick={() => handlePlay(candidate)}>Play</Button>
150+
) : masterPlayerStatus === "paused" ? (
151+
<Button onClick={() => handlePlay(candidate)}>Play</Button>
152+
) : (
153+
<Button onClick={() => handlePause()}>Pause</Button>
154+
)}{" "}
155+
{/* {feed && candidate.array[0].playlistTimestamp ? (
156+
<PlaybarPlayer
132157
feed={feed}
133158
timestamp={startPlaylistTimestamp}
134159
startOffset={startOffset}
@@ -140,7 +165,7 @@ export default function CandidateCard(props: {
140165
/>
141166
) : candidate.array[0].audioUri ? (
142167
<>
143-
<CandidateCardAIPlayer
168+
<PlaybarAIPlayer
144169
audioUri={candidate.array[0].audioUri}
145170
// index={props.index}
146171
onPlayerInit={onPlayerInit}
@@ -157,7 +182,7 @@ export default function CandidateCard(props: {
157182
startPlaylistTimestamp +
158183
" feed: " +
159184
feed
160-
)}
185+
)} */}
161186
</Box>
162187
<Link
163188
// href needs a slash before so it isn't relative to folder path

ui/src/components/CandidatesList.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import ReportsBarChart from "@/components/ReportsBarChart";
1515
import SearchBar from "@/components/SearchBar";
1616
import { useData } from "@/context/DataContext";
1717
import { useLayout } from "@/context/LayoutContext";
18+
import { useNowPlaying } from "@/context/NowPlayingContext";
1819
import { Feed } from "@/graphql/generated";
1920
import { Candidate, CombinedData } from "@/types/DataTypes";
2021

@@ -169,6 +170,8 @@ export default function CandidatesList() {
169170
isSuccess: boolean;
170171
} = useData();
171172

173+
const { setQueue } = useNowPlaying();
174+
172175
const layout = useLayout();
173176

174177
const lgUp = useMediaQuery((theme: Theme) => theme.breakpoints.up("lg"));
@@ -242,7 +245,7 @@ export default function CandidatesList() {
242245
// }));
243246
// };
244247

245-
const [playNext, _setPlayNext] = useState(true);
248+
// const [playNext, _setPlayNext] = useState(true);
246249

247250
const players = useRef({});
248251

@@ -320,7 +323,8 @@ export default function CandidatesList() {
320323
? sortDescending([...candidates])
321324
: sortAscending([...candidates]);
322325
setSortedCandidates(sorted);
323-
}, [candidates, sortOrder, isSuccess]);
326+
setQueue(sorted);
327+
}, [candidates, sortOrder, isSuccess, setQueue]);
324328

325329
const candidateCards = sortedCandidates.map(
326330
(candidate: Candidate, index: number) => (
@@ -336,7 +340,7 @@ export default function CandidatesList() {
336340
// command={playing.index === index ? "play" : "pause"}
337341
feeds={feeds}
338342
players={players}
339-
playNext={playNext}
343+
// playNext={playNext}
340344
/>
341345
),
342346
);

ui/src/components/PlayBar.tsx

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { AppBar, Toolbar } from "@mui/material";
2+
import { useEffect, useMemo, useState } from "react";
3+
4+
import { useData } from "@/context/DataContext";
5+
import { useNowPlaying } from "@/context/NowPlayingContext";
6+
7+
import { PlaybarAIPlayer } from "./Player/PlaybarAIPlayer";
8+
import { PlaybarPlayer } from "./Player/PlaybarPlayer";
9+
10+
export default function PlayBar() {
11+
const { nowPlaying } = useNowPlaying();
12+
const { feeds } = useData();
13+
14+
const [playerProps, setPlayerProps] = useState({
15+
feed: feeds.length > 0 ? feeds[0] : null,
16+
image: feeds.length > 0 ? feeds[0].imageUrl : "",
17+
playlist: 0,
18+
startOffset: 0,
19+
endOffset: 0,
20+
audioUri: "",
21+
});
22+
23+
useEffect(() => {
24+
const candidateArray = nowPlaying.array;
25+
if (candidateArray && candidateArray.length > 0) {
26+
const firstDetection = candidateArray[candidateArray.length - 1];
27+
const lastDetection = candidateArray[0];
28+
const feed = feeds.find((feed) => feed.id === firstDetection.feedId);
29+
console.log("feed is: " + JSON.stringify(feed, null, 2));
30+
31+
const playlist =
32+
candidateArray.length > 0
33+
? Math.min(...candidateArray.map((d) => +d.playlistTimestamp))
34+
: 0;
35+
36+
const offsetPadding = 15;
37+
const minOffset =
38+
candidateArray.length > 0
39+
? Math.min(...candidateArray.map((d) => +d.playerOffset))
40+
: 0;
41+
42+
// ensure that the last offset is still in the same playlist -- future iteration may pull a second playlist if needed
43+
const firstPlaylist = candidateArray.filter(
44+
(d) => +d.playlistTimestamp === playlist,
45+
);
46+
47+
const maxOffset =
48+
firstPlaylist.length > 0
49+
? Math.max(...firstPlaylist.map((d) => +d.playerOffset))
50+
: 0;
51+
const startOffset = Math.max(0, minOffset - offsetPadding);
52+
const endOffset = maxOffset + offsetPadding;
53+
54+
if (feed) {
55+
setPlayerProps({
56+
feed: feed ? feed : feeds[0],
57+
image: feed ? feed.imageUrl : feeds[0].imageUrl,
58+
playlist: playlist,
59+
startOffset: startOffset,
60+
endOffset: endOffset,
61+
audioUri: "",
62+
});
63+
}
64+
65+
if (lastDetection.audioUri) {
66+
setPlayerProps((p) => ({
67+
...p,
68+
feed: null,
69+
playlist: 0,
70+
startOffset: 0,
71+
endOffset: 0,
72+
audioUri: lastDetection.audioUri,
73+
}));
74+
}
75+
}
76+
}, [nowPlaying, feeds]);
77+
78+
const clipDateTime = useMemo(() => {
79+
if (nowPlaying?.array) {
80+
return new Date(nowPlaying?.array[0].timestamp).toLocaleString();
81+
} else {
82+
return "";
83+
}
84+
}, [nowPlaying]);
85+
86+
const clipNode = useMemo(() => {
87+
if (nowPlaying?.array) {
88+
return nowPlaying?.array[0]?.hydrophone;
89+
} else {
90+
return "";
91+
}
92+
}, [nowPlaying]);
93+
94+
useEffect(() => {
95+
console.log("candidate is: " + JSON.stringify(nowPlaying, null, 2));
96+
});
97+
98+
return (
99+
<AppBar
100+
position="fixed"
101+
color="secondary"
102+
sx={{
103+
// Keep header above the side drawer
104+
zIndex: (theme) => theme.zIndex.drawer + 1,
105+
bottom: 0,
106+
top: "auto",
107+
height: "87px",
108+
display: "flex",
109+
justifyContent: "center",
110+
alignItems: "center",
111+
}}
112+
>
113+
<Toolbar
114+
sx={{
115+
width: "100%",
116+
}}
117+
>
118+
{nowPlaying.array && playerProps.feed ? (
119+
<>
120+
<PlaybarPlayer
121+
feed={playerProps.feed}
122+
image={playerProps.image?.toString()}
123+
playlistTimestamp={playerProps.playlist}
124+
startOffset={playerProps.startOffset}
125+
endOffset={playerProps.endOffset}
126+
key={playerProps.startOffset + "-" + playerProps.endOffset}
127+
clipDateTime={clipDateTime}
128+
clipNode={clipNode}
129+
/>
130+
</>
131+
) : nowPlaying.array && playerProps.audioUri.length > 0 ? (
132+
<>
133+
<PlaybarAIPlayer
134+
image={playerProps.image?.toString()}
135+
audioUri={playerProps.audioUri}
136+
key={playerProps.audioUri}
137+
clipDateTime={clipDateTime}
138+
clipNode={clipNode}
139+
/>
140+
</>
141+
) : !nowPlaying.array ||
142+
(nowPlaying.array &&
143+
!playerProps.feed &&
144+
!playerProps.audioUri.length) ? (
145+
"Press play on any candidate to activate Play bar"
146+
) : (
147+
"Something is wrong"
148+
)}
149+
</Toolbar>
150+
</AppBar>
151+
);
152+
}

0 commit comments

Comments
 (0)