From 9350346b4cc7679af39357e36b32a9a49d820df5 Mon Sep 17 00:00:00 2001 From: Riley Smith Date: Mon, 31 Jan 2022 00:30:29 -0800 Subject: [PATCH 1/4] state stuff in backend --- backend/.idea/workspace.xml | 7 +--- backend/internal/ws/handlers.go | 18 +++++++--- backend/internal/ws/hub.go | 4 +++ backend/internal/ws/state.go | 46 +++++++++++++++++++++++++ frontend/src/components/Player.tsx | 20 +++++------ frontend/src/interfaces/SocketEvents.ts | 1 + frontend/src/pages/player.tsx | 8 +---- 7 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 backend/internal/ws/state.go diff --git a/backend/.idea/workspace.xml b/backend/.idea/workspace.xml index ab20c62..8f3dc7f 100644 --- a/backend/.idea/workspace.xml +++ b/backend/.idea/workspace.xml @@ -5,16 +5,11 @@ - - + - - - - - - + @@ -101,4 +100,15 @@ true + + + + + file://$PROJECT_DIR$/internal/ws/handlers.go + 61 + + + + \ No newline at end of file diff --git a/backend/internal/ws/handlers.go b/backend/internal/ws/handlers.go index d018a8d..d17e9a4 100644 --- a/backend/internal/ws/handlers.go +++ b/backend/internal/ws/handlers.go @@ -15,7 +15,7 @@ func handleIdentifyEvent(message *Message) { user := d["user"].(map[string]interface{}) userId := user["id"].(string) playhead := message.hub.State.playhead - + paused := message.hub.State.paused m := Message{ MessageData: MessageData{ Type: Identify, @@ -24,6 +24,7 @@ func handleIdentifyEvent(message *Message) { "playlist": "http://localhost:8081/BelleOpening.m3u8", "hasController": message.hub.State.IsController(userId), "playhead": playhead, + "paused": paused, "user": d["user"], }, }, @@ -62,6 +63,10 @@ func handleSetPlayhead(message *Message) { if err != nil { log.Errorf("unable to set playhead. %s", err) } + err = message.hub.State.setPaused(d["paused"].(bool)) + if err != nil { + log.Errorf("unable to set paused. %s", err) + } for client := range message.Client.hub.Clients { if client == message.Client { continue diff --git a/backend/internal/ws/state.go b/backend/internal/ws/state.go index 8e97abe..ee50984 100644 --- a/backend/internal/ws/state.go +++ b/backend/internal/ws/state.go @@ -27,11 +27,19 @@ func (s *State) setPlayhead(playhead float64) error { return errors.New("unable to find state") } s.Lock() - defer s.Lock() + defer s.Unlock() s.playhead = playhead return nil } - +func (s *State) setPaused(paused bool) error { + if s == nil { + return errors.New("unable to find state") + } + s.Lock() + defer s.Unlock() + s.paused = paused + return nil +} func (s *State) IsController(userID string) bool { if userID == s.controllerUserId { return true diff --git a/frontend/src/components/Player.tsx b/frontend/src/components/Player.tsx index ebd43d2..52ef0ed 100644 --- a/frontend/src/components/Player.tsx +++ b/frontend/src/components/Player.tsx @@ -1,6 +1,7 @@ import { Box, css } from "@chakra-ui/react"; -import React, { FC, useRef, useState } from "react"; +import React, { FC, useEffect, useRef, useState } from "react"; import ReactPlayer, { Config, ReactPlayerProps } from "react-player"; +import IdentityData from "../interfaces/Identity"; import { MessageTypes } from "../interfaces/IMessage"; import SocketEvents from "../interfaces/SocketEvents"; import Message from "../util/Message"; @@ -10,6 +11,7 @@ import PlayerSocket from "../ws/websocket"; type PlayerProps = { id: string; socket: PlayerSocket; + identity?: IdentityData; } & ReactPlayerProps; const Player: FC = (props) => { @@ -21,10 +23,20 @@ const Player: FC = (props) => { forceHLS: true, }, }; - socket.emitter.on(SocketEvents.GetPlayhead, (e) => { + useEffect(() => { + if (playerRef.current && typeof props.identity !== "undefined") { + console.log(props.identity.playhead); + playerRef.current.seekTo(props.identity.playhead); + setPaused(props.identity.paused); + } + }, []); + socket.emitter.once(SocketEvents.SetPlayhead, (e) => { + console.log(e); playerRef.current.seekTo(e.playhead); + setPaused(e.paused); }); const onSeek = (playedSeconds: number) => { + if (!props.identity.admin) return; if (paused) { socket?.send( MessageUtil.encode( @@ -46,6 +58,7 @@ const Player: FC = (props) => { ); }; const onPause = () => { + if (!props.identity.admin) return; setPaused(true); socket?.send( MessageUtil.encode( @@ -57,6 +70,7 @@ const Player: FC = (props) => { ); }; const onPlay = () => { + if (!props.identity.admin) return; setPaused(false); socket?.send( MessageUtil.encode( @@ -74,11 +88,12 @@ const Player: FC = (props) => { width="100%" height="100%" config={config} - controls + controls={props.identity.admin} onPlay={onPlay} onPause={onPause} onSeek={onSeek} ref={playerRef} + playing={!paused} {...props} /> diff --git a/frontend/src/interfaces/Identity.ts b/frontend/src/interfaces/Identity.ts index 2867307..1e5b0f2 100644 --- a/frontend/src/interfaces/Identity.ts +++ b/frontend/src/interfaces/Identity.ts @@ -5,7 +5,8 @@ interface IdentityData { controller?: boolean; clientID?: string; playlist?: string; - playHead?: number; + playhead?: number; + paused?: boolean; user: IUser; } diff --git a/frontend/src/interfaces/SocketEvents.ts b/frontend/src/interfaces/SocketEvents.ts index 9b25b0a..408d0a9 100644 --- a/frontend/src/interfaces/SocketEvents.ts +++ b/frontend/src/interfaces/SocketEvents.ts @@ -1,5 +1,6 @@ enum SocketEvents { Identify = "Identify", + SetPlayhead = "SetPlayhead", GetPlayhead = "GetPlayhead", } diff --git a/frontend/src/pages/player.tsx b/frontend/src/pages/player.tsx index 608fb27..a71eade 100644 --- a/frontend/src/pages/player.tsx +++ b/frontend/src/pages/player.tsx @@ -28,6 +28,7 @@ const PlayerPage: NextPage = ({ user }) => { setID(e.playlist); setIdentity(e); }); + console.log(identity); } return ( <> @@ -35,7 +36,7 @@ const PlayerPage: NextPage = ({ user }) => { Watch Together - + ); From 7a7e99337f74a058d0f87eec7f2c8178c73cd89f Mon Sep 17 00:00:00 2001 From: Riley Smith Date: Mon, 31 Jan 2022 12:29:06 -0800 Subject: [PATCH 3/4] websocket is more stable --- backend/.idea/workspace.xml | 13 --- frontend/src/components/Player.tsx | 150 ++++++++++++---------------- frontend/src/hooks/useWS.ts | 8 +- frontend/src/interfaces/Playhead.ts | 4 + frontend/src/pages/index.tsx | 2 +- frontend/src/pages/player.tsx | 35 ++++--- frontend/src/util/Handler.ts | 1 - frontend/src/util/api.tsx | 3 - frontend/src/util/index.ts | 6 ++ frontend/src/util/isBrowser.ts | 3 - frontend/src/util/isDev.ts | 3 - frontend/src/ws/websocket.ts | 5 +- 12 files changed, 106 insertions(+), 127 deletions(-) create mode 100644 frontend/src/interfaces/Playhead.ts delete mode 100644 frontend/src/util/Handler.ts delete mode 100644 frontend/src/util/api.tsx create mode 100644 frontend/src/util/index.ts delete mode 100644 frontend/src/util/isBrowser.ts delete mode 100644 frontend/src/util/isDev.ts diff --git a/backend/.idea/workspace.xml b/backend/.idea/workspace.xml index d0c22a3..bd3ec1e 100644 --- a/backend/.idea/workspace.xml +++ b/backend/.idea/workspace.xml @@ -6,8 +6,6 @@ - - @@ -100,15 +98,4 @@ true - - - - - file://$PROJECT_DIR$/internal/ws/handlers.go - 61 - - - - \ No newline at end of file diff --git a/frontend/src/components/Player.tsx b/frontend/src/components/Player.tsx index 52ef0ed..f4e7c49 100644 --- a/frontend/src/components/Player.tsx +++ b/frontend/src/components/Player.tsx @@ -1,103 +1,85 @@ -import { Box, css } from "@chakra-ui/react"; -import React, { FC, useEffect, useRef, useState } from "react"; +import { Box } from "@chakra-ui/react"; +import React, { forwardRef } from "react"; import ReactPlayer, { Config, ReactPlayerProps } from "react-player"; -import IdentityData from "../interfaces/Identity"; -import { MessageTypes } from "../interfaces/IMessage"; -import SocketEvents from "../interfaces/SocketEvents"; -import Message from "../util/Message"; -import MessageUtil from "../util/MessageUtil"; -import PlayerSocket from "../ws/websocket"; -type PlayerProps = { - id: string; - socket: PlayerSocket; - identity?: IdentityData; -} & ReactPlayerProps; - -const Player: FC = (props) => { - const playerRef = useRef(null); - const [paused, setPaused] = useState(false); - const { socket } = props; +const Player = forwardRef((props, ref) => { const config: Config = { file: { forceHLS: true, }, }; - useEffect(() => { - if (playerRef.current && typeof props.identity !== "undefined") { - console.log(props.identity.playhead); - playerRef.current.seekTo(props.identity.playhead); - setPaused(props.identity.paused); - } - }, []); - socket.emitter.once(SocketEvents.SetPlayhead, (e) => { - console.log(e); - playerRef.current.seekTo(e.playhead); - setPaused(e.paused); - }); - const onSeek = (playedSeconds: number) => { - if (!props.identity.admin) return; - if (paused) { - socket?.send( - MessageUtil.encode( - new Message(MessageTypes.SetPlayhead, { - playhead: playedSeconds, - paused: true, - }) - ) - ); - return; - } - socket?.send( - MessageUtil.encode( - new Message(MessageTypes.SetPlayhead, { - playhead: playedSeconds, - paused: false, - }) - ) - ); - }; - const onPause = () => { - if (!props.identity.admin) return; - setPaused(true); - socket?.send( - MessageUtil.encode( - new Message(MessageTypes.SetPlayhead, { - playhead: playerRef.current.getCurrentTime(), - paused: true, - }) - ) - ); - }; - const onPlay = () => { - if (!props.identity.admin) return; - setPaused(false); - socket?.send( - MessageUtil.encode( - new Message(MessageTypes.SetPlayhead, { - playhead: playerRef.current.getCurrentTime(), - paused: false, - }) - ) - ); - }; + // useEffect(() => { + // if (playerRef.current && typeof props.identity !== "undefined") { + // console.log(props.identity.playhead); + // playerRef.current.seekTo(props.identity.playhead); + // setPaused(props.identity.paused); + // } + // }, []); + // socket.emitter.once(SocketEvents.SetPlayhead, (e) => { + // console.log(e); + // playerRef.current.seekTo(e.playhead); + // setPaused(e.paused); + // }); + // const onSeek = (playedSeconds: number) => { + // if (!props.identity.admin) return; + // if (paused) { + // socket?.send( + // MessageUtil.encode( + // new Message(MessageTypes.SetPlayhead, { + // playhead: playedSeconds, + // paused: true, + // }) + // ) + // ); + // return; + // } + // socket?.send( + // MessageUtil.encode( + // new Message(MessageTypes.SetPlayhead, { + // playhead: playedSeconds, + // paused: false, + // }) + // ) + // ); + // }; + // const onPause = () => { + // if (!props.identity.admin) return; + // setPaused(true); + // socket?.send( + // MessageUtil.encode( + // new Message(MessageTypes.SetPlayhead, { + // playhead: playerRef.current.getCurrentTime(), + // paused: true, + // }) + // ) + // ); + // }; + // const onPlay = () => { + // if (!props.identity.admin) return; + // setPaused(false); + // socket?.send( + // MessageUtil.encode( + // new Message(MessageTypes.SetPlayhead, { + // playhead: playerRef.current.getCurrentTime(), + // paused: false, + // }) + // ) + // ); + // }; return ( ); -}; +}); export default Player; diff --git a/frontend/src/hooks/useWS.ts b/frontend/src/hooks/useWS.ts index 2a4c80c..546d336 100644 --- a/frontend/src/hooks/useWS.ts +++ b/frontend/src/hooks/useWS.ts @@ -7,17 +7,19 @@ interface useWSProps { } // todo write websocket reconnector -const useWS = ({ user }: useWSProps) => { +const useWS = ({ user }: useWSProps): PlayerSocket | null => { if (typeof window === "undefined") { return; } // todo checkout usecallback - const [socket, setSocket] = useState(); + const [socket, setSocket] = useState(null); useEffect(() => { let internalSocket = new PlayerSocket(user); setSocket(internalSocket); return () => { - return internalSocket.close(); + if (internalSocket.readyState !== WebSocket.OPEN) { + return internalSocket.close(); + } }; }, []); diff --git a/frontend/src/interfaces/Playhead.ts b/frontend/src/interfaces/Playhead.ts new file mode 100644 index 0000000..13dc7b4 --- /dev/null +++ b/frontend/src/interfaces/Playhead.ts @@ -0,0 +1,4 @@ +export default interface SetPlayheadEvent { + playhead: number; + paused: boolean; +} diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 3dae921..56e2719 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -8,7 +8,7 @@ import { Container } from "../components/Container"; import { Footer } from "../components/Footer"; import { Hero } from "../components/Hero"; import { Main } from "../components/Main"; -import isDev from "../util/isDev"; +import { isDev } from "../util"; const Index: NextPage = () => { return ( diff --git a/frontend/src/pages/player.tsx b/frontend/src/pages/player.tsx index a71eade..0938f60 100644 --- a/frontend/src/pages/player.tsx +++ b/frontend/src/pages/player.tsx @@ -1,16 +1,17 @@ import { GetServerSideProps, NextPage } from "next"; import { User } from "next-auth"; import { getSession } from "next-auth/react"; -import dynamic from "next/dynamic"; import Head from "next/head"; -import React, { useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; +import ReactPlayer from "react-player"; import { Container } from "../components/Container"; +import Player from "../components/Player"; import useWS from "../hooks/useWS"; import IdentityData from "../interfaces/Identity"; +import SetPlayheadEvent from "../interfaces/Playhead"; import SocketEvents from "../interfaces/SocketEvents"; -import isBrowser from "../util/isBrowser"; - -const Player = dynamic(() => import("../components/Player"), { ssr: false }); +import { isBrowser } from "../util"; +import PlayerSocket from "../ws/websocket"; interface PlayerPageProps { user: User; @@ -18,25 +19,29 @@ interface PlayerPageProps { // types for the function const PlayerPage: NextPage = ({ user }) => { - // const playerRef = useRef(); const socket = useWS({ user }); + const playerRef = useRef(); const [id, setID] = useState(""); const [identity, setIdentity] = useState(); - if (isBrowser() && typeof socket !== "undefined") { - socket.emitter.on(SocketEvents.Identify, (e: IdentityData) => { - console.log(e); - setID(e.playlist); - setIdentity(e); - }); - console.log(identity); - } + useEffect(() => { + if (isBrowser() && typeof socket !== "undefined") { + socket?.emitter.once(SocketEvents.Identify, (e: IdentityData) => { + setID(e.playlist); + setIdentity(e); + }); + socket?.emitter.on(SocketEvents.SetPlayhead, (e: SetPlayheadEvent) => { + console.log(e); + }); + } + }, [socket]); + const onSeek = () => {}; return ( <> Watch Together - + ); diff --git a/frontend/src/util/Handler.ts b/frontend/src/util/Handler.ts deleted file mode 100644 index 43d2e2e..0000000 --- a/frontend/src/util/Handler.ts +++ /dev/null @@ -1 +0,0 @@ -export default class Handler {} diff --git a/frontend/src/util/api.tsx b/frontend/src/util/api.tsx deleted file mode 100644 index cd48e7e..0000000 --- a/frontend/src/util/api.tsx +++ /dev/null @@ -1,3 +0,0 @@ -const useAPI = () => {}; - -export default useAPI; diff --git a/frontend/src/util/index.ts b/frontend/src/util/index.ts new file mode 100644 index 0000000..6b42b9e --- /dev/null +++ b/frontend/src/util/index.ts @@ -0,0 +1,6 @@ +export function isDev() { + return process.env.NODE_ENV === "development"; +} +export function isBrowser() { + return typeof window !== "undefined"; +} diff --git a/frontend/src/util/isBrowser.ts b/frontend/src/util/isBrowser.ts deleted file mode 100644 index cc6ba88..0000000 --- a/frontend/src/util/isBrowser.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default function isBrowser() { - return typeof window !== "undefined"; -} diff --git a/frontend/src/util/isDev.ts b/frontend/src/util/isDev.ts deleted file mode 100644 index 99f94cd..0000000 --- a/frontend/src/util/isDev.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default function isDev() { - return process.env.NODE_ENV === "development"; -} diff --git a/frontend/src/ws/websocket.ts b/frontend/src/ws/websocket.ts index 6f346ac..73709ee 100644 --- a/frontend/src/ws/websocket.ts +++ b/frontend/src/ws/websocket.ts @@ -25,6 +25,10 @@ export default class PlayerSocket extends Websocket { this.onmessage = this.onMessage; this.onclose = this.onClose; } + close(code?: number, reason?: string): void { + this.emitter.removeAllListeners(); + super.close(code, reason); + } onMessage(evt: MessageEvent) { let message = MessageUtil.decode(evt.data); if (message.type === MessageTypes["Ping"]) { @@ -65,7 +69,6 @@ export default class PlayerSocket extends Websocket { } onClose(event: CloseEvent) { console.log("[WS] socket connection closed"); - console.log(event); this.emitter.emit("closed"); } get open() { From 2586e60f0ce92eece901dd5e2649f899409b41b5 Mon Sep 17 00:00:00 2001 From: Riley Smith Date: Tue, 1 Feb 2022 17:07:56 -0800 Subject: [PATCH 4/4] finally fixed player --- backend/.idea/workspace.xml | 1 - frontend/src/components/Player.tsx | 2 - frontend/src/hooks/useWS.ts | 3 ++ frontend/src/interfaces/Identity.ts | 2 +- frontend/src/pages/player.tsx | 59 +++++++++++++++++++++++++++-- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/backend/.idea/workspace.xml b/backend/.idea/workspace.xml index bd3ec1e..b1c5f85 100644 --- a/backend/.idea/workspace.xml +++ b/backend/.idea/workspace.xml @@ -6,7 +6,6 @@ -