feat(ws): barebones SetPlayhead event

This commit is contained in:
vel 2022-01-27 22:48:21 -08:00
parent 5f81ed1756
commit 728f2d67d0
Signed by: velvox
GPG Key ID: 1C8200C1D689CEF5
11 changed files with 162 additions and 31 deletions

View File

@ -5,14 +5,17 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="8a64704d-5500-41a6-aa4c-e275933fc58c" name="Changes" comment=""> <list default="true" id="8a64704d-5500-41a6-aa4c-e275933fc58c" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/../.vscode/settings.json" beforeDir="false" afterPath="$PROJECT_DIR$/../.vscode/settings.json" afterDir="false" /> <change afterPath="$PROJECT_DIR$/cmd/watchtogether/build.sh" afterDir="false" />
<change afterPath="$PROJECT_DIR$/../frontend/src/interfaces/SocketEvents.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/watchtogether/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/watchtogether/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/internal/ws/handlers.go" beforeDir="false" afterPath="$PROJECT_DIR$/internal/ws/handlers.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/internal/ws/handlers.go" beforeDir="false" afterPath="$PROJECT_DIR$/internal/ws/handlers.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/package.json" afterDir="false" /> <change beforePath="$PROJECT_DIR$/internal/ws/hub.go" beforeDir="false" afterPath="$PROJECT_DIR$/internal/ws/hub.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/src/hooks/useWS.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/hooks/useWS.ts" afterDir="false" /> <change beforePath="$PROJECT_DIR$/internal/ws/message.go" beforeDir="false" afterPath="$PROJECT_DIR$/internal/ws/message.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/src/components/Container.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/components/Container.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/src/components/Player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/components/Player.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/src/interfaces/Identity.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/interfaces/Identity.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" afterDir="false" /> <change beforePath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/src/ws/websocket.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/ws/websocket.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../frontend/yarn.lock" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/yarn.lock" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />

View File

@ -0,0 +1,12 @@
#!/bin/bash
VERSION=v1.0.0
COMMIT_HASH=$(git rev-parse --short HEAD)
ENVIRONMENT=$ENV
BUILDSTRING="$VERSION-$COMMIT_HASH-$ENVIRONMENT"
echo The environment is "$ENVIRONMENT"
echo Building watch together backend "$VERSION-$COMMIT_HASH"
if [[ "$ENVIRONMENT" == "dev" ]]; then
go build -gcflags="all=-N -l" -ldflags "-X github.com/qpixel/watchtogether/cmd/watchtogether/main.VERSION=${BUILDSTRING} -X github.com/qpixel/watchtogether/cmd/watchtogether/main.ENVIRONMENT=${ENVIRONMENT}";
else
go build -ldflags "-X github.com/qpixel/watchtogether/cmd/watchtogether/main.VERSION=${BUILDSTRING} -X github.com/ubergeek77/uberbot/v2/core.ENVIRONMENT=${ENVIRONMENT}";
fi

View File

@ -10,6 +10,11 @@ import (
var log = tlog.NewTaggedLogger("Logger", tlog.NewColor("38;5;111")) var log = tlog.NewTaggedLogger("Logger", tlog.NewColor("38;5;111"))
var (
VERSION string
ENVIRONMENT string
)
func main() { func main() {
r := mux.NewRouter() r := mux.NewRouter()
hub := ws.NewHub() hub := ws.NewHub()
@ -47,7 +52,8 @@ func main() {
} }
return return
}) })
log.Infof("starting backend")
log.Infof("running version %s. environment: %s", VERSION, ENVIRONMENT)
err := http.ListenAndServe(":8080", r) err := http.ListenAndServe(":8080", r)
if err != nil { if err != nil {
log.Errorf("%s", err.Error()) log.Errorf("%s", err.Error())

View File

@ -16,10 +16,11 @@ func handleIdentifyEvent(message *Message) {
MessageData: MessageData{ MessageData: MessageData{
Type: Identify, Type: Identify,
Data: map[string]interface{}{ Data: map[string]interface{}{
"admin": true, "admin": true,
"playlist": "http://cdnapi.kaltura.com/p/1878761/sp/187876100/playManifest/entryId/1_usagz19w/flavorIds/1_5spqkazq,1_nslowvhp,1_boih5aji,1_qahc37ag/format/applehttp/protocol/http/a.m3u8", "playlist": "http://localhost:8081/BelleOpening.m3u8",
"playhead": 0, "controller": true,
"user": d["user"], "playhead": 0,
"user": d["user"],
}, },
}, },
} }
@ -42,5 +43,21 @@ func handleGetPlayhead(message *Message) {
} }
func handleSetPlayhead(message *Message) { func handleSetPlayhead(message *Message) {
d := message.Data.(map[string]interface{})
m := Message{
MessageData: MessageData{
Type: SetPlayhead,
Data: map[string]interface{}{
"playhead": d["playhead"],
"paused": d["paused"],
},
},
}
log.Infof("Received SetPlayhead event. playhead is at %s", d["playhead"])
for client := range message.Client.hub.Clients {
if client == message.Client {
continue
}
client.send <- m.SerializeMessage().Data
}
} }

View File

@ -30,9 +30,9 @@ func (h *Hub) handleMessage(rm RawMessage) {
handleIdentifyEvent(&m) handleIdentifyEvent(&m)
case Ping: case Ping:
handlePingEvent(&m) handlePingEvent(&m)
case Position: case GetPlayhead:
handleGetPlayhead(&m) handleGetPlayhead(&m)
case SetPosition: case SetPlayhead:
handleSetPlayhead(&m) handleSetPlayhead(&m)
default: default:
return return

View File

@ -14,8 +14,8 @@ const (
Ping MessageTypes = iota Ping MessageTypes = iota
Pong Pong
Identify Identify
Position GetPlayhead
SetPosition SetPlayhead
) )
type MessageData struct { type MessageData struct {

View File

@ -1,19 +1,23 @@
import { Flex, useColorMode, FlexProps } from '@chakra-ui/react' import React, { FC } from "react";
import { Flex, useColorMode, FlexProps } from "@chakra-ui/react";
export const Container = (props: FlexProps) => { export const Container: FC<FlexProps> = (props: FlexProps) => {
const { colorMode } = useColorMode() const { colorMode } = useColorMode();
const bgColor = { light: 'gray.50', dark: 'gray.900' } const bgColor = { light: "gray.50", dark: "gray.900" };
const color = { light: 'black', dark: 'white' } const color = { light: "black", dark: "white" };
return ( return (
<Flex <Flex
direction="column" direction="column"
alignItems="center"
justifyContent="flex-start" justifyContent="flex-start"
bg={bgColor[colorMode]} bg={bgColor[colorMode]}
color={color[colorMode]} color={color[colorMode]}
{...props} {...props}
/> />
) );
} };
Container.defaultProps = {
alignItems: "center",
};

View File

@ -1,15 +1,90 @@
import React, { FC } from "react"; import { Box, css } from "@chakra-ui/react";
import React, { FC, useRef, useState } from "react";
import ReactPlayer, { Config, ReactPlayerProps } from "react-player"; import ReactPlayer, { Config, ReactPlayerProps } from "react-player";
import { MessageTypes } from "../interfaces/IMessage";
import Message from "../util/Message";
import MessageUtil from "../util/MessageUtil";
import PlayerSocket from "../ws/websocket";
type PlayerProps = { id: string } & ReactPlayerProps; type PlayerProps = {
id: string;
socket: PlayerSocket;
} & ReactPlayerProps;
interface ProgressProps {
played: number;
playedSeconds: number;
loaded: number;
loadedSeconds: number;
}
const Player: FC<PlayerProps> = (props) => { const Player: FC<PlayerProps> = (props) => {
const playerRef = useRef<ReactPlayer>(null);
const [paused, setPaused] = useState<boolean>(false);
const { socket } = props;
const config: Config = { const config: Config = {
file: { file: {
forceHLS: true, forceHLS: true,
}, },
}; };
return <ReactPlayer url={props.id} config={config} {...props} />; const onProgress = (state: ProgressProps) => {
if (paused) {
socket?.send(
MessageUtil.encode(
new Message(MessageTypes.SetPlayhead, {
playhead: state.playedSeconds,
paused: true,
})
)
);
}
socket?.send(
MessageUtil.encode(
new Message(MessageTypes.SetPlayhead, {
playhead: state.playedSeconds,
paused: false,
})
)
);
};
const onPause = () => {
setPaused(true);
socket?.send(
MessageUtil.encode(
new Message(MessageTypes.SetPlayhead, {
playhead: playerRef.current.getCurrentTime(),
paused: true,
})
)
);
};
const onPlay = () => {
setPaused(false);
socket?.send(
MessageUtil.encode(
new Message(MessageTypes.SetPlayhead, {
playhead: playerRef.current.getCurrentTime(),
paused: false,
})
)
);
};
return (
<Box height="100vh" width="100vw">
<ReactPlayer
url={props.id}
width="100%"
height="100%"
config={config}
controls
onPlay={onPlay}
onPause={onPause}
onProgress={onProgress}
ref={playerRef}
{...props}
/>
</Box>
);
}; };
export default Player; export default Player;

View File

@ -1,6 +1,8 @@
import IUser from "./IUser"; import IUser from "./IUser";
interface IdentityData { interface IdentityData {
admin?: boolean;
controller?: boolean;
clientID?: string; clientID?: string;
playlist?: string; playlist?: string;
playHead?: number; playHead?: number;

View File

@ -0,0 +1,5 @@
enum SocketEvents {
Identify = "Identify",
}
export default SocketEvents;

View File

@ -1,3 +1,4 @@
import { Socket } from "dgram";
import { GetServerSideProps, NextPage } from "next"; import { GetServerSideProps, NextPage } from "next";
import { User } from "next-auth"; import { User } from "next-auth";
import { getSession } from "next-auth/react"; import { getSession } from "next-auth/react";
@ -5,36 +6,42 @@ import dynamic from "next/dynamic";
import Head from "next/head"; import Head from "next/head";
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
import ReactPlayer from "react-player"; import ReactPlayer from "react-player";
import { BaseReactPlayerProps } from "react-player/base";
import { Container } from "../components/Container"; import { Container } from "../components/Container";
import useWS from "../hooks/useWS"; import useWS from "../hooks/useWS";
import IdentityData from "../interfaces/Identity"; import IdentityData from "../interfaces/Identity";
import { MessageTypes } from "../interfaces/IMessage"; import { MessageTypes } from "../interfaces/IMessage";
import SocketEvents from "../interfaces/SocketEvents";
import isBrowser from "../util/isBrowser"; import isBrowser from "../util/isBrowser";
import Message from "../util/Message";
import MessageUtil from "../util/MessageUtil";
const Player = dynamic(() => import("../components/Player"), { ssr: false }); const Player = dynamic(() => import("../components/Player"), { ssr: false });
interface PlayerPageProps { interface PlayerPageProps {
user: User; user: User;
} }
// types for the function
const PlayerPage: NextPage<PlayerPageProps> = ({ user }) => { const PlayerPage: NextPage<PlayerPageProps> = ({ user }) => {
const playerRef = useRef<ReactPlayer>(); // const playerRef = useRef<ReactPlayer>();
const socket = useWS({ user }); const socket = useWS({ user });
const [id, setID] = useState<string>(""); const [id, setID] = useState<string>("");
const [identity, setIdentity] = useState<IdentityData>();
if (isBrowser() && typeof socket !== "undefined") { if (isBrowser() && typeof socket !== "undefined") {
socket.emitter.on("Identify", (e: IdentityData) => { socket.emitter.on(SocketEvents.Identify, (e: IdentityData) => {
console.log(e); console.log(e);
setID(e.playlist); setID(e.playlist);
setIdentity(e);
}); });
} }
return ( return (
<> <>
<Head> <Head>
<title>Watch Together</title> <title>Watch Together</title>
</Head> </Head>
<Container height="100vh"> <Container height="100vh" background={"#000"}>
<Player id={id} ref={playerRef} /> <Player id={id} socket={socket} />
</Container> </Container>
</> </>
); );